How to prevent partial writes, duplicate work and unsafe retries in Python APIs that coordinate databases and external services.
Define the atomic unit first, make retries explicit and keep irreversible side effects outside ambiguous transaction boundaries.
The boundary is a business decision
A transaction boundary defines which state changes must succeed or fail as one unit. It is not simply the place where a framework happens to call commit. For an order workflow, the boundary might include reserving inventory, creating the order and recording an outbox event. If one of those writes fails, none of them should become visible as completed work.
Route-level commits make this hard to see. A handler may write a shipment, commit it, call a carrier and then send an email. When the carrier call times out, the client sees a failure even though the shipment already exists. A retry can create a second shipment because the database and the external operation were never part of one explicit protocol.
- Name the business operation whose consistency you are protecting.
- Keep commit and rollback ownership in one application-level boundary.
- Do not let repositories or helpers commit independently without making that behaviour explicit.
Database atomicity does not include the network
A database transaction cannot roll back an HTTP request that has already reached a payment, shipping or email provider. Holding a transaction open while waiting on the network is not a solution: it increases lock time, consumes connections and still cannot make two independent systems atomic.
A common repair is to commit local state and an outbox record in the same database transaction. A separate worker then delivers the external action and records its result. This changes an impossible cross-system transaction into a recoverable workflow with explicit states, retries and observability.
- Persist the intent to perform external work alongside the business change.
- Give background delivery a retry policy, terminal failure state and operational visibility.
- Design compensation only where the business can actually reverse an earlier action.
Idempotency makes retries safe
Retries are unavoidable. Clients retry after timeouts, workers redeliver messages and operators replay failed jobs. Idempotency means repeating the same logical request produces the same effect instead of creating additional effects.
An idempotency key must be scoped to the operation and protected by a uniqueness constraint. Store the key with the operation result inside the same transaction as the state change. Checking first and inserting later without a constraint leaves a race in which concurrent requests both pass the check.
- Define who creates the key and how long its meaning remains valid.
- Reject reuse of a key with a materially different request payload.
- Return the recorded result for a completed duplicate and a clear state for work still in progress.
Evidence for the production gate
A correctness review should trace at least one important operation from request to durable state and every external side effect. The evidence is stronger when failure injection demonstrates what happens before commit, after commit and during redelivery.
The gate is not passed because a happy-path test uses a transaction fixture. It is passed when partial failure has a defined state, concurrent retries cannot duplicate the operation and an engineer can safely recover unfinished work.
Explore all engineering notes.
Use PRODUCTION-7 to connect this concern with the other dimensions of a trustworthy backend.
View all articles Get the checklist