DynamoDB Locking: Optimistic and Pessimistic Approaches

Written by Lakindu Hewawasam
Last updated: October 23rd, 2026
DynamoDB optimistic locking uses a version attribute and a conditional write to reject changes based on stale data. For an explicit acquire-and-release lock, use an application-level lease, such as the Amazon DynamoDB Lock Client. Transactions provide atomic changes across items, but they do not hold a lock while your application reads data, waits for a user, or calls an external service.
Introduction to DynamoDB locking
A single DynamoDB write is atomic. The concurrency problem appears when an application reads an item, computes a new value, and writes later: another writer can change the item between those requests. An unconditional replacement can then discard that writer's work.
For example, two editors read version 3 of a note. Both submit a different revision. If each write requires version 3 and changes the version to 4, only one can succeed. The other editor receives a conflict instead of silently overwriting the first edit.
Not every update needs a version. An atomic counter or a condition such as sufficient inventory can express the rule directly in one request. Start with the smallest conditional write that protects the business invariant.
Optimistic locking
What is optimistic locking?
Optimistic locking detects competing edits when you save. Readers do not reserve the item or block other readers. The application carries the version it read and includes that expected version in the write's ConditionExpression.
Use an integer version that changes on every accepted edit. A timestamp is useful audit metadata, but a wall-clock timestamp can repeat or move backward. It is a poor default concurrency token when several writers can update within the same clock tick.
How does optimistic locking work?
- Create the item with a version, using a create-only condition.
- Read the item and retain its version with the editable data.
- Save changes only if the stored version still equals that value.
- Increment the version in the same write.
- On
ConditionalCheckFailedException, reload or show a conflict. Reapply the business decision to fresh data before trying again.
A strongly consistent read reduces the chance of starting with stale data, but it cannot prevent another writer from changing the item afterward. The write condition is what prevents a lost update.
How to use optimistic locking with SDK v3
Create the example notes table with a string partition key named note-id:
The following JavaScript uses the v3 document client. Run the initialization once; its condition intentionally rejects subsequent attempts to create the same note.
To exercise the conflict path, run two reads before either update, then submit both writes with the same expected version. Alternatively, pass a deliberately stale version. Do not make the production code silently replace :expected with the latest version while retaining an old full-item edit.

An accepted write advances the version. Another request with the old version now fails, including one that tries to delete the item if you apply the same condition to DeleteItem.

The same version check with boto3
This function takes the version the editor originally read. It returns the saved note or raises the conflict for the caller to handle.
ReturnValuesOnConditionCheckFailure can return the item that caused the condition to fail, saving a separate diagnostic read. Its response representation depends on the SDK interface, so inspect the error shape before treating it as a native object.
Java 2.x Enhanced Client version attributes
For new Java applications, use the DynamoDB Enhanced Client and @DynamoDbVersionAttribute. Its default VersionedRecordExtension adds the version checks and increments for supported writes. A mapped bean for the same table can declare:
The extension starts newly versioned items at 0 by default and increments by 1; both are configurable. Read the bean through the Enhanced Client, retain its version, modify it and call the mapped table's updateItem. If you configure custom extensions, explicitly retain VersionedRecordExtension, because custom extension configuration replaces the defaults.
The annotation does not automatically protect deleteItem; supply your own delete condition. AWS documents these behaviors in Enhanced Client extensions.
@DynamoDBVersionAttribute, with a capital DB, belongs to the legacy Java 1.x DynamoDBMapper. It is not the Java 2.x annotation. A migration should check save and delete behavior rather than only rename the import.
Benefits and limits of optimistic locking
Optimistic locking is a good fit for editable forms and other read-modify-write workflows with occasional conflicts. It needs no lease table or heartbeat, and readers proceed normally. Under heavy contention, repeated failed conditions cost capacity and application work; an atomic expression, queue or different data model may be more useful than retrying every collision.
Every writer must follow the protocol. A script that replaces an item without checking or advancing its version can invalidate the guarantee. Also plan for deletion and recreation: reusing the same key and resetting its version can make a very old edit look current. A separate immutable generation identifier can distinguish those lifetimes.
Pessimistic locking
What is pessimistic locking?
An application-level pessimistic lock reserves a resource before work begins. All participants agree to acquire the lock first and release it afterward. DynamoDB does not expose a SQL-style SELECT FOR UPDATE session that you keep open while your application works.
This is useful for coordinating a job or an external resource. A lock item does not automatically block ordinary writes to another DynamoDB item; every participating writer must honor it.
Amazon DynamoDB Lock Client: current status
The Amazon DynamoDB Lock Client is a Java library, with AmazonDynamoDBLockClient as its main class. Checked on October 23, 2026, the repository is not archived, its README describes community support, and its current examples use the AWS SDK for Java 2.x. These observations do not imply an AWS support SLA or a release cadence.
AWS's distributed locking guidance still points to this library. That guidance does not name a replacement lock client. The Enhanced Client replaces DynamoDBMapper for object mapping; it is not a successor to this distributed locking library.
How lease-based locks work
The lock owner acquires a resource key with a conditional write, renews ownership through heartbeats, and releases it in a finally block. A contender can take over when the lease protocol establishes that the previous owner stopped renewing.
The Java client's README describes a record-version token and elapsed lease observation, rather than relying on deletion of a TTL attribute. A custom absolute-expiry design must separately account for clock skew. In either design, renewal and release must verify ownership, so a delayed old worker cannot renew or delete a new owner's lock.
Time to Live is optional cleanup for expired lock records in a custom design. Deletion typically happens within a few days, so waiting for the item to disappear would turn a short lease into a long outage. Evaluate lease expiry in the acquisition condition and keep the TTL cleanup deadline separate if the protocol needs longer-lived records.
A paused worker may resume after its lease has expired. Stop work when renewal fails and use fencing where the protected resource supports it: the resource rejects operations carrying an obsolete ownership generation. Lock acquisition alone cannot make an external side effect exactly once.
Benefits and tradeoffs of pessimistic locks
Locks can reduce duplicate work when recomputing a long operation is expensive. They also add acquisition latency, heartbeat costs and failure recovery. Keep the protected section short, establish a consistent acquisition order if you need multiple locks, and monitor renewal failures and time spent waiting.
High contention does not automatically make leases the right answer. A single highly contested lock serializes every caller. A queue or partitioned ownership may fit the workload better.
Transactions are an alternative, not a held application lock
Use TransactWriteItems when several DynamoDB changes must commit together. Put the expected version or state condition directly on the transaction's update. A transaction without that condition does not know which earlier read your application used.
For a note revision plus an audit record, transact an update of the current note and a put of a distinct audit item. If the version condition fails, neither change commits. Transaction conflicts remain possible and need handling; transactions do not eliminate conflicts or hold a reservation between requests.
Multi-Region eventual-consistency global tables reconcile concurrent writes using last-writer-wins behavior, so local version conditions do not provide a cross-Region lock. Route a lock authority to one Region or design a protocol for your selected consistency mode. MRSC global tables support strongly consistent cross-Region reads but do not support transactions or TTL.
Optimistic vs pessimistic locking: what should you use?
| Requirement | Starting point |
|---|---|
| Reject an editor's stale changes | Version condition |
| Increment a counter or change one item based on its current state | Atomic UpdateItem expression, with a condition where needed |
| Commit related changes to several items | Transaction with explicit business conditions |
| Coordinate a long-running task or external resource | Lease-based lock with renewal, ownership checks and stale-owner handling |
| Retain every historical revision | An explicit version-history model |
Frequently Asked Questions
Does DynamoDB have locking by default?
How does DynamoDB optimistic locking work?
Is TransactWriteItems a pessimistic lock?
Can TTL release a DynamoDB lock on time?
Is the Amazon DynamoDB Lock Client deprecated?
Dynobase is a Professional GUI Client for DynamoDB
Start your 7-day free trial today