Optimistic vs Pessimistic Locking in Web Development
Choosing between optimistic and pessimistic locking directly dictates how an application balances data integrity against system throughput under heavy traffic. This article examines the core mechanics of both concurrency control strategies, evaluating their impact on database performance, latency, developer complexity, and user experience, while providing clear criteria for selecting the appropriate approach for modern web architectures.
Understanding the Mechanics
Concurrency conflicts occur when two or more transactions attempt to read and mutate the same piece of state simultaneously. Locking strategies prevent race conditions such as lost updates or dirty writes.
- Optimistic Locking: Assumes that record collisions
are rare. Instead of placing physical locks on records during read
operations, it tracks changes using an identifier, such as an
incrementing
versionnumber or atimestamp. When committing a write, the system checks whether the version has changed since it was read. If the version matches, the update succeeds and the version increments. If it does not match, the transaction fails or triggers a retry. - Pessimistic Locking: Assumes that conflicts will
happen frequently. It places an exclusive lock on the target data at the
moment of reading (for example, using SQL's
SELECT ... FOR UPDATE). Other transactions attempting to access or modify that specific record are forced to wait in a queue until the holding transaction either commits or rolls back.
Database Throughput and Latency
The choice of lock fundamentally affects database resource allocation and response times.
Optimistic Locking
Optimistic locking is entirely non-blocking for read operations. Database connections are held only for the brief duration of the read and the subsequent write, not during intermediate processing. In low-to-moderate contention environments, this yields significantly higher throughput and predictable latency.
However, in scenarios with extreme contention (such as a flash sale on a single SKU), optimistic locking degrades rapidly. Because high-frequency writes constantly invalidate the version token, transactions repeatedly fail and trigger retries, resulting in wasted CPU cycles and network overhead.
Pessimistic Locking
Pessimistic locking guarantees that once a transaction acquires a lock, its write operation will succeed. This eliminates the CPU overhead associated with transaction retries.
The downside is thread and connection starvation. While a transaction holds a lock, all competing transactions must wait. In high-concurrency systems, this quickly exhausts database connection pools, causes request latency to spike, and introduces the risk of deadlocks, where two transactions hold locks the other needs, requiring database-level intervention to abort one of the workers.
Architectural Fit for Modern Web Apps
Modern web applications are predominantly stateless, operating over HTTP request-response cycles.
Optimistic locking naturally aligns with stateless architectures and
microservices. Because the version check happens strictly at the point
of update, state does not need to be pinned to a specific database
connection across multiple user steps or network hops. A REST API can
simply accept an entity payload containing the version_id
and handle conflicts gracefully using HTTP
409 Conflict.
Pessimistic locking requires an active, open database transaction to maintain the lock. Holding database locks across network boundaries or user think-time is an anti-pattern that leads to cascading system failures. Consequently, pessimistic locks must be scoped tightly within discrete backend execution blocks, requiring careful transaction design.
When to Use Which Strategy
Use Optimistic Locking When:
- The system is read-heavy: Scenarios such as content management systems, user profile edits, and document collaboration, where multiple users rarely update the exact same field at the exact same millisecond.
- Architecture is stateless: Environments where transactions cannot span multiple API calls.
- Conflict cost is low: Workflows where notifying the user that "the record was updated by someone else" and asking them to refresh is acceptable.
Use Pessimistic Locking When:
- Contention is consistently high: Scenarios such as inventory countdowns for high-demand ticket sales, where hundreds of requests attempt to claim the same slot per second.
- The cost of rollback is prohibitive: Critical financial transactions or banking balances, where duplicate or retried operations carry heavy business, legal, or computational penalties.
- Operations are short and predictable: Backend processes that can acquire the lock, mutate the value, and commit the transaction within a few milliseconds to avoid queuing downstream workers.