Skip to content

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:

  1. Client sends request.
  2. Server processes it (side effect committed).
  3. Response is lost on the return path (TCP reset, timeout, upstream proxy failure).
  4. Client sees failure and retries.
  5. 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-Key is attached.
  • Otherwise, surface the failure.
  • Circuit breakers remain applicable; they eliminate retry load regardless.

Seen in

Last updated · 550 distilled / 1,221 read