An idempotency key identifies a repeated operation; If-Match requires a current representation validator before a state-changing request.
Spring retry and stale-write checks: choose the right HTTP contract
The two questions are different
POST with key intake-41 and body 42 returns the same receipt on replay in the source kit. PUT with a stale receipt ETag returns 412 and does not overwrite the reviewed state. Neither rule authenticates the caller. A tenant can know an ETag or guess a weak key while lacking permission to update the resource.
For create requests, define key scope, canonical payload, retention and the response to an in-flight duplicate. For update requests, define whether the API requires a strong ETag and how a client refreshes it after 412. The local fixtures handle one quoted tag and one in-memory idempotency map, not the full protocol grammar or a distributed race.
Put the checks in the right order
Authenticate and authorize the caller, parse and bound the request, then make the replay or revision decision inside the durable write boundary. If a user is denied, do not reveal another tenant's stored replay response. If the database update and replay record commit separately, a timeout can still create duplicate work. Outbox writes cover later event delivery, not HTTP replay by themselves.
Checked source
// Both branches are checked by separate source-kit tests.
if (prior != null && !prior.request().equals(request)) return ResponseEntity.status(409).build();
if (!currentTag.equals(expectedTag)) return ResponseEntity.status(412).build();Verification boundary
IdempotentReceiptPostTest.replayReturnsTheOriginalReceiptAndConflictingPayloadIsRejected and ReceiptIfMatchWriteTest.staleClientCannotOverwriteTheCurrentRevision runs in the downloadable Spring source kit. The excerpt leaves out surrounding setup and imports; the kit contains the complete test.
Costs and limits
The excerpt is a decision sketch, not a combined deployable handler. The two tests prove distinct local cases. They do not prove ordering across a real security filter, database transaction, HTTP proxy or concurrent replicas.
Common Mistakes
- Do not treat an ETag as an authorization token.
- Do not use If-Match as a substitute for create-request replay storage.
- Do not return another tenant's recorded response based only on a matching key.
Read next
Spring POST idempotency keys: replay the same receipt result, Spring If-Match writes: reject a stale receipt revision, Spring method authorization: reject a cross-tenant read.
