Menu
Dev.to #systemdesign·March 25, 2026

Ensuring Idempotency in Distributed Transactions

This article discusses the critical issue of idempotency in distributed systems, particularly in financial transactions where duplicate processing can lead to incorrect states. It highlights why databases alone cannot guarantee idempotency across retries and outlines a common application-level pattern using unique transaction IDs to prevent double execution.

Read original on Dev.to #systemdesign

The Idempotency Problem in Distributed Systems

In distributed systems, especially those handling financial or state-changing operations, network issues or client retries can lead to the same request being processed multiple times. Without proper handling, this can cause significant data inconsistencies, such as incorrect balances in an account transfer scenario. The article illustrates this with a simple example where a duplicate transfer leads to an incorrect state where Alice's balance is too low and Bob's is too high.

Why Databases Alone Aren't Enough

While databases provide ACID properties (Atomicity, Consistency, Isolation, Durability) for individual transactions, they typically do not inherently track or prevent the duplicate execution of an *application-level request*. Each submission, even if identical to a previous one, is treated as a new, distinct operation by the database. The database ensures the internal consistency of each transaction but lacks the context to identify and reject duplicate *requests* from the application layer.

ℹ️

Database vs. Application Responsibility

Database Responsibility: Ensures correctness and consistency *per transaction* (no partial updates, valid data state). Application Responsibility: Ensures that an *application-level request* is not repeated, even if the underlying database transaction is idempotent for a single execution.

Solution: Application-Level Idempotency with Unique IDs

The standard solution involves implementing idempotency at the application layer. This is achieved by assigning a unique transaction ID (also known as an idempotency key) to each client request. Before processing any state-changing operation, the application checks if a request with that specific transaction ID has already been executed. If it has, the duplicate request is rejected or the result of the original request is returned.

pseudocode
function processTransfer(transactionId, senderId, receiverId, amount):
  if (isTransactionProcessed(transactionId)):
    return existingResult(transactionId)
  
  // Begin database transaction
  deductFromSender(senderId, amount)
  addToReceiver(receiverId, amount)
  markTransactionAsProcessed(transactionId, status)
  // Commit database transaction

  return successResult
idempotencytransactionsdistributed transactionsdata consistencyerror handlingretriesapplication design

Comments

Loading comments...