Course contentsShow
← Node.js
Lesson 1506 of 1,98013. Error Handling and Resilient SystemsFree lesson

Recovering Incomplete Database Writes

You will use secondary verification steps to ensure that interrupted database writes are consistent with application state.

In this lesson, you will learn how to implement secondary verification steps to bridge the gap between application state and database persistence when a write operation is interrupted.

The Uncertainty of Distributed Writes

When a Node.js process sends a command to a database, the connection can drop, the process might suffer an OOM Killed event, or a network partition may occur. Even if your code receives an error, you often don't know if the database received the request, processed the transaction, or failed halfway through. Relying on the outcome of the initial call is insufficient for resilient systems.

Implementing Secondary Verification

To recover, you must treat your application state as the source of truth and perform a reconciliation check. Instead of blindly retrying a write, your logic should follow a "Check-Before-Write" pattern.

async function safeUpdateUserBalance(userId, amount, idempotencyKey) {
  // 1. Check if the specific transaction already exists
  const existingTx = await db.transactions.findOne({ idempotencyKey });
  
  if (existingTx) {
    // Already successful, reconcile state
    return existingTx.status === 'COMPLETED' ? existingTx : await retryTx(existingTx);
  }

  // 2. Perform the write with the key
  try {
    await db.transactions.insert({ userId, amount, idempotencyKey, status: 'PENDING' });
    await db.users.updateBalance(userId, amount);
    await db.transactions.update({ idempotencyKey }, { status: 'COMPLETED' });
  } catch (err) {
    // Handle error (e.g., log, trigger alert)
  }
}

By introducing an idempotencyKey and a dedicated transaction log, you create a secondary verification step. If the process terminates, a rebooted service can query the transaction log to see if the write was partially committed. If the log shows PENDING but the user balance hasn't been updated, your system can trigger an automated remediation process to finalize or roll back the specific record based on the current state.

This approach shifts your architecture from "fire-and-forget" to a state-aware model where the database and your application state are continuously reconciled.

Key Takeaway: Never assume a failed write is a non-event; always use idempotency keys and secondary lookups to verify the actual state of your data before attempting any recovery.