{"post":{"slug":"optimistic-vs-pessimistic-locking-2026","title":"Optimistic vs pessimistic locking in 2026: concurrency control","excerpt":"Optimistic locking assumes conflicts are rare and checks at write time; pessimistic locking blocks others up front. This 2026 guide compares the two concurrency strategies and when each one fits.","category":"code","date":"June 30, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">W</span>hen two users try to change the same record at the same time, something has to decide who wins and how conflicts are handled. Optimistic and pessimistic locking are the two classic answers. Pessimistic locking assumes conflicts are likely and blocks others up front; optimistic locking assumes conflicts are rare and only checks for them at the moment of writing. Picking the wrong one for your workload leads to either needless slowdowns or surprise lost updates.\n\n## What changed in 2026\n\n- **Distributed systems pushed optimistic patterns.** As apps spread across regions, holding long database locks became impractical, so version-based optimistic concurrency and conflict resolution grew more common.\n- **ORMs made both easy.** Mainstream frameworks now ship first-class support for version columns and row locks, so choosing a strategy is a config decision more than hand-coded work.\n- **Serverless amplified lock cost.** Short-lived functions holding pessimistic locks can starve each other, nudging teams toward optimistic approaches with retries.\n\n## How pessimistic locking works\n\nBefore reading a record it intends to change, a transaction acquires a lock (for example, `SELECT ... FOR UPDATE`). Anyone else who wants that row waits until the lock is released. This guarantees no one else can modify the data in the meantime, so lost updates are impossible — but other transactions block, and if locks are held too long you get contention, timeouts, or deadlocks.\n\n## How optimistic locking works\n\nNo lock is taken while reading. Instead, each row carries a version number or timestamp. When you go to save, the update says, in effect, \"set these values *where version is still what I read*.\" If someone else changed the row first, the version no longer matches, zero rows update, and your write is rejected. You then retry with fresh data. No one waits — but conflicts cost a redo.\n\n## Side by side\n\n| Factor | Optimistic | Pessimistic |\n| --- | --- | --- |\n| Assumption | Conflicts are rare | Conflicts are likely |\n| Locks held | None until commit | From read to commit |\n| Cost of conflict | Retry the write | Others wait |\n| Risk | Wasted work on retries | Contention, deadlocks |\n| Best for | High read, low contention | Hot rows, high contention |\n\n## When to use which\n\nUse optimistic locking when contention is low and reads dominate — most web apps, where two people rarely edit the exact same record at the exact same moment. The occasional retry is cheaper than locking everyone out. Because concurrent writes are really a form of [race condition](/blog/race-conditions-explained-2026), the same mental model applies here. Use pessimistic locking for genuinely hot rows where collisions are frequent and a lost-update retry is expensive or unsafe: inventory counts, seat booking, and financial balances.\n\n## Common pitfalls\n\n- **Optimistic without retry logic.** If you reject a conflicting write but do not retry, you just moved the failure to the user. Handle the retry.\n- **Pessimistic locks held across user think-time.** Never hold a database lock while waiting for a human to fill out a form. That is a recipe for contention.\n- **Ignoring deadlocks.** Pessimistic locking can deadlock when transactions grab rows in different orders. Lock in a consistent order.\n- **No version column.** Optimistic concurrency needs a version or timestamp field; comparing every column is fragile.\n\n## FAQ\n\n**Which is faster, optimistic or pessimistic locking?**\nUnder low contention, optimistic wins because nothing blocks. Under high contention, optimistic wastes effort on retries and pessimistic can be more efficient.\n\n**Does optimistic locking prevent lost updates?**\nYes — the version check rejects a write based on stale data. You still need retry logic to recover gracefully.\n\n**Is `SELECT ... FOR UPDATE` optimistic or pessimistic?**\nPessimistic. It acquires a row lock immediately so other transactions must wait.\n\n**Can I mix both in one application?**\nAbsolutely. Use optimistic locking for most records and pessimistic locking on the few genuinely hot rows. The right tool depends on the row, not the whole app.\n\n## Where to go next\n\n- [CAP theorem explained in 2026](/blog/cap-theorem-explained-2026)\n- [Eventual consistency explained in 2026](/blog/eventual-consistency-explained-2026)\n- [Load balancing algorithms in 2026](/blog/load-balancing-algorithms-2026)","hue":320,"metaTitle":"Optimistic vs Pessimistic Locking 2026 Guide","metaDescription":"Optimistic vs pessimistic locking 2026: optimistic checks versions at write time, pessimistic blocks up front. How each works and when to use it.","keywords":["optimistic vs pessimistic locking 2026","optimistic locking explained","pessimistic locking explained","optimistic vs pessimistic concurrency","database locking strategies","select for update locking","version column optimistic concurrency","concurrency control 2026"],"tldr":["**Optimistic assumes conflicts are rare** — no locks; a version check rejects stale writes so you retry.","**Pessimistic assumes conflicts are likely** — it locks the row up front so others wait.","**Low contention favors optimistic** — high contention or hot rows favor pessimistic.","**Optimistic needs retry logic** — rejecting a stale write without retrying just moves the failure to the user.","**Skip:** holding a pessimistic lock across user think-time — never lock a row while waiting on a human."],"readTime":"3 min read","wordCount":695},"related":[{"slug":"database-transactions-explained-2026","title":"Database transactions explained in 2026: ACID, isolation, and real examples","excerpt":"Transactions are the mechanism that keeps your database consistent when multiple operations run concurrently. This is the 2026 guide to ACID properties, isolation levels, and the patterns that prevent data corruption.","category":"code","date":"April 13, 2026","author":"ByteLedger Team","readTime":"6 min read","hue":318},{"slug":"transactions-explained-2026","title":"Database transactions explained in 2026: ACID, isolation, and beyond","excerpt":"Transactions are the safety net of every database write. This 2026 guide covers ACID, isolation levels, deadlocks, and the patterns that prevent data corruption.","category":"code","date":"April 21, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":310},{"slug":"database-isolation-levels-2026","title":"Database Isolation Levels: The Bugs You Only Get Under Load","excerpt":"Your code is correct when one user runs it. Two users at once produce a result neither would accept. Isolation levels decide which of those anomalies you get.","category":"code","date":"August 12, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":304}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"oauth-vs-jwt-2026","title":"OAuth vs JWT Explained 2026: Protocol vs Token Format","excerpt":"OAuth and JWT get compared as if they are rivals, but one is an authorization framework and the other is a token format. Here is the 2026 guide to what each is, how they work together, and common mistakes.","category":"code","date":"June 30, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":240},"older":{"slug":"parkinsons-law-2026","title":"Parkinsons Law in 2026: why work expands to fit time","excerpt":"Parkinsons Law says work expands to fill the time available for it. Here is the 2026 guide to what that really means, how to use tight deadlines on purpose, and where the idea gets oversold.","category":"guides","date":"June 30, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":320}}