Replay idempotent mutations across request canonicalization rollouts
Use a stored fingerprint version and effect-preserving request translation so retries across a schema rollout replay the original outcome without admitting changed intent.
Replay idempotent mutations across request canonicalization rollouts
When to use
Use this when durable idempotency records survive a request schema or canonicalization change and old clients may retry while new clients are sending requests.
Record identity
Persist the request schema version, fingerprint algorithm version, fingerprint, and immutable committed outcome with each idempotency record. Backfill legacy records with the exact historical version that produced their fingerprints. Keep the unique key scoped to caller and operation, independent of schema version. A version in the unique key would permit a duplicate effect across the rollout.
Duplicate-key rule
The committed row is authoritative. On a retry, read its recorded versions and translate the incoming request into that row's historical semantic domain. Translation is allowed only when it preserves the entire intended effect. Compute exactly the fingerprint recorded by that historical algorithm and compare it with the row. Reject a mismatch or an untranslatable request as key reuse. Never accept because a fingerprint matches under some other available version, and never drop a newly meaningful field merely to manufacture a match.
For example, version two adds a fee with a default of zero. A version-two retry with fee zero may represent a version-one operation; one with a nonzero fee cannot. The inverse translation fills in zero when a version-one retry meets a version-two record. This is safe only if zero truly preserves the operation's effect.
Concurrent requests and replay
The unchanged unique constraint serializes old and new requests for one key. The transaction that commits first fixes the version and intent. After that transaction commits, the loser applies the stored row's comparison rule. A matching loser replays the immutable outcome, and a changed loser is rejected. If the winner rolls back, the other request may claim the key. Return the original stored response for the same contract, or render the immutable outcome into a compatible response contract after identity matches. Do not rerun the mutation or derive the supposed original result from mutable current state.
Rollout order
Deploy readers that understand old and new records before enabling new-version writes. Backfill version metadata before ambiguous rows are compared. Retain old comparison code for the full record retention and retry horizon. If a legacy row lacks enough evidence to establish intent, fail closed rather than guessing.