Optimistic concurrency
Notation follows the APIs index.
- APIs that update resources
SHOULDimplement optimistic concurrency to detect concurrent modifications. Those that doMUSTdo so using aversionfield markedrequiredand notreadOnly. - The
versionfieldSHOULDbe a simple integer that is incremented after every successful update but, regardless of what value is used, the fieldMUSTbe formatted as a string. - If the value provided does not match the stored version, the API
MUSTrespond409 Conflict. - Upon successfully updating the resource, the API
MUSTupdate the version identifier.
Where we depart from an RFC
RFC 9110 conditional requests use an ETag response
header, an If-Match request header, and 412 Precondition Failed on a mismatch. These standards
instead use a version field in the resource, sent back on update, and 409 Conflict on a
mismatch. They depart from conditional requests because:
- Sanctioned by the same RFC. RFC 9110 describes this exact case under
409: a representation sent withPUTthat conflicts with an earlier change, where versioning is in use. Conditional requests are an alternative, not a requirement. - Simpler for callers. The version is part of the resource the caller already reads and sends back. There is no header to capture and replay, and generated clients expose it as an ordinary property.
- Works for bulk operations. A bulk replace carries many resources in one request, each with its
own version. One
If-Matchheader cannot. - Travels with the data. The version stays with the resource wherever the resource goes: events, local copies, logs, and caches.
- Nothing lost. A service can still return
ETagfor HTTP caching.