CONCEPT Cited by 1 source
Non-idempotent operations¶
Definition¶
A non-idempotent operation is one whose effect changes with each execution: retrying a successful-but-ambiguously-failed call risks duplicate side effects (double-charged users, duplicate order creation, double-incremented counters, duplicate notifications).
Zalando's timeouts post names the risk explicitly:
"Non-idempotent operations can cause unintended side effects if retried multiple times. Examples include operations that modify data, perform financial transactions, or have irreversible consequences. Retrying such operations can lead to data inconsistency or duplicate actions." (Source: sources/2023-07-25-zalando-all-you-need-to-know-about-timeouts)
The ambiguous-failure problem¶
The failure mode is not "the request failed, so retrying does harm" — that case is obvious. The hard case is:
- Client sends request.
- Server processes it (side effect committed).
- Response is lost on the return path (TCP reset, timeout, upstream proxy failure).
- Client sees failure and retries.
- Server processes the retry — a second side effect.
The client cannot distinguish "my request never reached the server" from "my request reached the server, took effect, but the response was lost." Without additional contract, retry is unsafe.
Examples¶
Canonical non-idempotent operations:
- POST /orders { ... } — each call creates a new order.
- POST /transfers { from, to, amount } — each call moves
money.
- UPDATE accounts SET balance = balance - 100 WHERE id = 42 —
each call debits again.
- INSERT INTO events (…) — each call appends a row.
- SEND email TO user — each call delivers another copy.
Two mitigations¶
The Zalando post names both:
1. Idempotency-Key header (recommended). Caller supplies a unique key per logical operation; server deduplicates by key; on retry, server returns the cached response instead of reprocessing.
"For safely retrying requests without accidentally performing the same operation twice, consider supporting additional Idempotency-Key header in your API. When creating or updating an object, use an idempotency key. Then, if a connection error occurs, you can safely repeat the request without the risk of creating a second object or performing the update twice."
Named references: Stripe's Idempotent Requests, Amazon Builders' Library — Making retries safe with idempotent APIs.
2. Do not retry. If the operation is non-idempotent and the API provides no idempotency-key contract, the correct response to a timeout is not a retry — it is failing upward, surfacing the ambiguous state to the caller (or user), and letting them decide. A retry on a non-idempotent operation without a deduplication contract is, at production scale, guaranteed to cause duplicates.
Relationship to the Zalando retry policy¶
Zalando's house-style retry rule — patterns/retry-on-5xx-not-4xx (retry on 5xx and timeouts, not on 4xx) — is only safe under the assumption the operation is idempotent. For non-idempotent operations, the policy must be amended:
- Retry only if an
Idempotency-Keyis attached. - Otherwise, surface the failure.
- Circuit breakers remain applicable; they eliminate retry load regardless.
Seen in¶
- sources/2023-07-25-zalando-all-you-need-to-know-about-timeouts — canonical framing as the retry-risk class requiring an idempotency-key contract before retries become safe.
Related¶
- concepts/idempotent-operations — the complement; retries are safe without additional safeguards.
- concepts/exponential-backoff-jitter — the retry- scheduling strategy once an operation has been made retry-safe.
- patterns/idempotency-key-header — the mitigation that promotes non-idempotent writes to safely retryable ones.
- patterns/retry-on-5xx-not-4xx — retry policy that must be gated on idempotency status.
- patterns/circuit-breaker — orthogonal safeguard that applies regardless of idempotency.