Optimistic Concurrency Control (OCC)
In plain English
Plain definition
A way to handle concurrent writes by not locking anything up front — you read data, compute a change, and only at commit time check whether someone else modified it first, retrying if they did.
OCC operates in three phases: read (capture data plus a version marker — a monotonic counter, timestamp, or content hash), compute (apply business logic locally, unlocked), and validate/commit (atomically check the version marker hasn’t changed and apply the write, typically via a compare-and-swap primitive). If validation fails, the transaction aborts and the caller retries from the read phase. This is the mechanism underneath etcd‘s Txn with Compare clauses, DynamoDB’s ConditionExpression on a version attribute, and CockroachDB/PostgreSQL serializable snapshot isolation’s abort-on-conflict behavior at commit.
OCC is frequently conflated with MVCC, but they solve different problems: MVCC is a storage-layer technique that lets readers see a consistent snapshot without blocking writers by retaining multiple versions of a row; OCC is a concurrency policy for how writers resolve contention against each other. Many systems layer OCC validation on top of an MVCC storage engine — the MVCC snapshot gives you the ‘read version’ for free, and OCC decides whether the eventual write is safe to commit against that version.
The critical failure mode is retry amplification under contention. As write concurrency on a hot key increases, the probability of validation failure grows superlinearly, and naive immediate retries create a thundering herd that increases contention further — a self-reinforcing livelock. Production implementations require exponential backoff with jitter, retry budgets, and often a fallback to pessimistic locking or request coalescing once retry counts exceed a threshold. A second subtle failure is version granularity mismatch: if the version marker covers an entire row when only one field changed, unrelated concurrent writers falsely conflict, causing unnecessary aborts — this pushes designs toward field-level or CRDT-based version vectors instead of a single row-level counter.
The architectural implication is that OCC shifts cost from the critical path (no lock held during computation) to the tail (aborts, retries, wasted CPU on discarded work), which makes it excellent for read-heavy, low-conflict workloads like configuration stores, leader-election CAS, and optimistic UI merge patterns, but a poor fit for high-contention counters or hot-partition write paths where pessimistic locking, sharded counters, or CRDTs amortize contention more predictably. Engineers evaluating OCC should model expected conflict rate explicitly rather than assuming ‘lock-free’ implies ‘faster’ — under sufficient contention, OCC’s wasted-work overhead can exceed the blocking cost of a well-tuned pessimistic lock.