Chapter 10 · Transaction Boundaries and Consistency by Design

Idempotency, Retries, and Safe State Transitions

Design idempotent commands, safe retries, and valid state transitions so duplicate requests and transient failures do not corrupt business state.

Beginner70–100 minutesIdempotency + resilient workflowsLast reviewed: August 2026

Learning outcomes

Reliable systems retry operations. Networks time out, clients reconnect, jobs restart, and users double-click. If the same logical command is processed twice, the database must not accidentally create duplicate orders, double-charge, consume inventory twice, or repeat state transitions.

01

Define idempotency for database-backed commands.

02

Use idempotency keys and uniqueness constraints to deduplicate retries.

03

Model valid state transitions explicitly.

04

Design retries that distinguish transient conflicts from permanent business failures.

What idempotency means

An operation is idempotent when applying the same logical request multiple times has the same business effect as applying it once.

Not the same as “same SQL can run twice”

This is not idempotent:

sql · example
INSERT INTO part_usage(...)VALUES (...);

because each retry creates another usage row.

Idempotency key

Clients can send a unique request key:

model · example
request_id = '8c31...'

The database stores it with a unique constraint:

model · example
UNIQUE (request_id)

Command deduplication

sql · example
CommandReceipt(  request_id PRIMARY KEY,  command_type,  resource_id,  result_code,  created_at)

On retry, the system can return the previously recorded result.

Idempotency must be transactional

Do not insert the business row and idempotency receipt in separate transactions. They must commit together or failure can create ambiguous retry behavior.

Rule

Deduplication metadata and the protected business effect belong in the same transaction whenever possible.

Commit uncertainty

A client sends a command, the server commits, but the response is lost. The client retries because it cannot know the outcome. Idempotency makes that retry safe.

Natural idempotency

Some operations are naturally idempotent:

model · example
SET archived = true

Running it twice leaves the same state.

Non-idempotent operations

These need more care:

model · example
balance = balance - 100quantity = quantity + 1INSERT paymentINSERT usage event

State transitions

Represent allowed transitions explicitly:

Do not allow arbitrary assignment of status values merely because they are in the valid domain.

Compare-and-set transition

sql · example
UPDATE work_orderSET status_code = 'closed'WHERE work_order_id = ?  AND status_code = 'in_progress';

If zero rows update, the transition is no longer valid or another transaction changed the state.

Transition history

model · example
WorkOrderStatusHistory(  status_history_id,  work_order_id,  from_status,  to_status,  changed_at,  changed_by,  request_id)

Including request_id makes duplicate transitions easier to detect and audit.

Retryable versus non-retryable failures

FailureTypical handling
Serialization failureRetry transaction.
Deadlock victimRetry transaction.
Transient network timeoutRetry with idempotency protection.
Unique business-key violationUsually permanent unless retry represents same request.
Invalid state transitionDo not blindly retry; refresh state.

Exponential backoff and jitter

When many clients retry immediately, they can create another contention spike. Backoff and random jitter reduce synchronized retry storms.

Exactly-once is usually an illusion across systems

Distributed messaging commonly provides at-least-once delivery or duplicate possibilities. Design consumers to be idempotent rather than assuming messages will appear exactly once.

Outbox + idempotent consumer

A robust workflow:

  1. commit business state and outbox message together;
  2. publish message, possibly more than once;
  3. consumer stores processed message ID uniquely;
  4. duplicate deliveries become harmless.

WorkshopHub example: RecordPartUsage

Client sends:

model · example
request_idwork_order_idpart_idquantitycharged_unit_price

Within one transaction:

  1. insert command receipt or reserve request_id;
  2. check WorkOrder state;
  3. decrement inventory atomically;
  4. insert PartUsage;
  5. store response metadata;
  6. commit.

Duplicate request outcome

If the same request_id arrives again, return the previously recorded result instead of consuming stock again.

Safe retry checklist

  • Can the operation be identified uniquely?
  • Are repeated effects prevented by a unique key or compare-and-set?
  • Are state transitions conditional on expected current state?
  • Are transaction failures classified as retryable or permanent?
  • Are external events delivered through a retry-safe mechanism?

Practice: payment command

Prevent double charge

A client calls CapturePayment, times out, and retries. What schema-level mechanism should exist?

Review answer

Use a client/business idempotency key with a unique constraint, store the logical payment attempt/result transactionally, and ensure retries retrieve or reuse the prior result rather than creating a second charge. The external payment provider should also receive a stable idempotency key if supported.

Summary and next chapter

Chapter 10 connected schema design to operational consistency. You can now model transaction boundaries, interpret ACID, protect invariants under concurrency, choose optimistic or pessimistic control, and design retry-safe idempotent state transitions. Chapter 11 moves into index-aware design: how indexes reshape physical schemas, selectivity, composite indexes, covering indexes, and the write/storage costs of indexing.

References

  • Martin Kleppmann, Designing Data-Intensive Applications.
  • Jim Gray and Andreas Reuter, Transaction Processing: Concepts and Techniques.
  • PostgreSQL documentation on transactions, locking, and serialization failures.
  • Pat Helland, writings on idempotency and distributed transactions.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.