Put the precondition inside the object write
Use supported atomic If-Match and If-None-Match writes while treating ETags as opaque validators rather than universal hashes or version histories.
On this page
The question for today
What you’ll understand
- Explain the race in a separate read-before-unconditional-write sequence.
- Distinguish current ETag equality from a unique historical version identity.
- Handle precondition failures and ambiguous outcomes without blind overwrites.
Reading an object, checking it locally, and later writing unconditionally leaves a race between the check and the update. A supported conditional write places the relevant predicate inside the service's write operation. That can coordinate one key, but the predicate's meaning matters. An ETag comparison concerns the service's current validator, not necessarily a cryptographic content digest, a unique revision counter, or proof that no intervening change occurred. Build the application's invariant around the exact documented condition.
A local check does not reserve the key
Suppose two publishers both read the same manifest, compute different successors, and then PUT them unconditionally. Strong consistency of each individual operation does not prevent one successful write from replacing the other. The application performed no atomic check that the manifest was still the observed state. Serializing the local code in one process does not fix competition from other processes or recovery workers. Move the predicate to a supported conditional API or use another coordination protocol.
In S3 general purpose bucket examples, If-None-Match with the wildcard can create only when the relevant current object does not exist, and If-Match compares the supplied ETag with the current object's validator for supported writes such as PutObject and CompleteMultipartUpload. Permission, signing, and API support still apply. In a versioned bucket, a current delete marker has the documented absence semantics for the create condition; an older retained version does not necessarily block creation of a new current object.
An ETag is not a universal content hash
Treat a returned ETag as an opaque value unless the exact upload and encryption contract documents a stronger interpretation. Multipart uploads and some encryption configurations do not produce a simple whole-object MD5 ETag. Even when an ETag matches a content digest in a particular case, it should not be promoted to a general-purpose version counter. Use an explicit supported checksum field and algorithm for integrity, and an actual version identifier or application revision protocol when historical identity matters.
Equality also has an ABA limitation: if content changes from A to B and back to an ETag-equivalent A, a predicate based only on that current validator may pass despite the intervening history. Some metadata changes can likewise fall outside what the ETag represents. The needed invariant might only be replace these current bytes, in which case that condition can suffice; another invariant may require no intervening revision. State which one the application needs and select evidence that actually expresses it.
A rejected condition is information to reconcile
A precondition failure tells the client its expected current state did not satisfy the write predicate. Read the new state and deliberately recompute, merge, or abandon rather than deleting the condition to make a retry succeed. Concurrent deletion and multipart operations have additional documented error cases and recovery requirements, so distinguish a failed predicate from a transport timeout with an unknown outcome. Store operation evidence needed to recognize whether the intended update already became visible.
The probability lab illustrates contention only under a synthetic independent-attempt model. Reset, set both p=0.3 as an assumed probability an attempt loses its precondition race and n=10, predict the number-of-conflicts distribution, then Play or Step. The lab does not execute compare-and-write operations or model retry feedback. Real publishers can synchronize and contend repeatedly, violating independence. Its bars help interpret an assumed failure count; they cannot certify a lock, a unique history, or a safe retry policy.
Work it through
Prevent a lost counter update under a stated predicate
A single object contains counter 5 with ETag e5. Two clients read it and each proposes 6 using If-Match e5. Assume the first successful content change produces a different ETag e6 and no other writes occur.
The first conditional write sees current e5, succeeds, and changes the counter to 6 with e6. Its predicate and write are one supported service operation.
The second client's e5 predicate no longer matches e6, so it must fail rather than silently overwrite. To apply a second increment, read 6/e6 and conditionally propose 7 under the same rules.
After that deliberate recomputation succeeds, the counter is 7. The reasoning depends on the stated distinct validator after the first change and does not claim immunity to arbitrary ABA histories or cross-key counter invariants.
The rejected stale write exposes competition so the second increment can be recomputed. Retrying its original value unconditionally would discard the intended two-increment semantics.
The living notebook
Explore the model
Interactive lab · probability
Count assumed conflicts without inventing a lock
Reset, set both synthetic per-attempt conflict probability p=0.3 and independent attempts n=10, predict the conflict-count bars, then Play or Step. No object writes, ETag checks, retry coordination, or provider consistency are simulated.
- Mean · np
- 5
- Variance · np(1−p)
- 2.5
- Total probability
- 1
These are exact binomial probabilities for independent trials with one fixed success probability. Playback tours the distribution; it does not resample outcomes. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forThe conditional operation enforces the current-state predicate; conflict statistics do not broaden that predicate into a historical guarantee.
Pause & reason
Check your understanding
Your turn
Try it yourself
A publisher receives a precondition failure while replacing a manifest. A proposed fix removes If-Match and repeats the old payload. Why can that break correctness, and what should the recovery decision use instead?
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
The rejected predicate identifies a competing state change. An unconditional retry abandons the very check intended to protect it.
Compare with the solution
The unconditional retry can overwrite another publisher's accepted manifest and lose its change. Recovery should inspect the current manifest and its validator or explicit revision, determine whether the original operation already happened, and then recompute, merge, or abandon according to the application's invariant. A fresh conditional write can protect that deliberate decision; making the request succeed is not sufficient evidence that its result is correct.
Keep the idea
Take it with you
- Supported conditional writes remove the race between a predicate and a single-key update.
- ETags are opaque validators, not universal MD5 digests or unique history counters.
- Precondition failures require deliberate reconciliation rather than unconditional replay.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.