Library / SDK
awslabs/amazon-dynamodb-lock-client avatar
awslabs/amazon-dynamodb-lock-client

amazon-dynamodb-lock-client: distributed locks and leader election on DynamoDB

The AmazonDynamoDBLockClient is a general purpose distributed locking library built on top of DynamoDB. It supports both coarse-grained and fine-grained locking.

521 stars92 forksJavaApache-2.0

At a glance

What is it?
The Amazon DynamoDB Lock Client is a Java library that turns a DynamoDB table into a lock service, with lease-based expiry, background heartbeats and locks keyed by arbitrary strings. Its practical value is leader election and per-entity mutual exclusion for systems already running on DynamoDB, and its practical cost is that lock acquisition deliberately waits out the lease before it can conclude anything.
Who is it for?
amazon-dynamodb-lock-client fits a Java service already provisioned on DynamoDB that needs one host to act as leader, or that must guarantee two workers never process the same customer or campaign at once, and that can accept DynamoDB as the coordination substrate. It does not fit a request path where a lock acquisition taking longer than one lease duration is unacceptable.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 116 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A lock service built out of a table you already have

The Amazon DynamoDB Lock Client is described as a general purpose distributed locking library built for DynamoDB. It supports both coarse-grained and fine-grained locking, and the reason it can do both is stated early: the lock keys can be any arbitrary string, up to a certain length.

That single design decision is what makes it more than a mutex for one resource. A coarse-grained lock is one global name everybody contends for. A fine-grained lock is one name per customer, per campaign, per device. With arbitrary string keys, the same library does both, and the granularity is a decision the caller makes at the call site rather than a property of the deployment.

The stated use case is specific enough to be useful. A distributed system needs to periodically do work on a given campaign, or a given customer, or any other object, and two boxes must not work on the same one at the same time. The README calls fine-grained locking a tough problem and presents this library as an attempt to simplify it on top of DynamoDB.

The audience is a Java team that already runs on DynamoDB and wants the coordination layer to live in the same system rather than to require a new piece of infrastructure. The project describes itself as open source and community supported, and points at the GitHub issue tracker for questions.

Leases and heartbeats decide whether a lock is still held

The lock model is lease based, and two mechanisms keep it alive.

A lease has a duration, set with withLeaseDuration in the options, and it is the window during which the lock is considered valid. The example configures 10 seconds with a heartbeat period of 3 seconds.

The heartbeat is the second mechanism. When the client is constructed with createHeartbeatBackgroundThread set to true, it spawns a background thread that continually updates the record version number on your locks to prevent them from expiring, doing it by calling the sendHeartbeat method in the lock client. The consequence is spelled out: as long as the JVM is running, your locks will not expire until you call releaseLock or lockItem.close.

The important detail is what happens when the JVM stops. The lease is what bounds the damage. The example prints the point in plain language: the lock expires in 10 seconds if the holder dies, and otherwise it is held until heartbeating stops. That is the classic lease trade-off, and here the numbers are yours to set: a longer lease means a longer worst-case wait after a crash, a shorter one means more heartbeats and more tolerance of slow work.

The locking mechanism itself lives in the record. The heartbeats update the record version number, which is how a later acquirer can tell a stale lock from a live one, and that check is what makes the polling rule described further down necessary.

acquireLock throws, tryAcquireLock returns an empty Optional

Two acquisition methods exist and the difference is in the failure behaviour, not in the mechanics.

tryAcquireLock returns Optional.absent() when the lock was not acquired. acquireLock throws a LockNotGrantedException instead. When shouldSkipBlockingWait is true, acquireLock can throw a LockCurrentlyUnavailableException if the lock is currently held and the client did not wait long enough to determine that it is stale, and tryAcquireLock catches both exceptions and returns Optional.absent().

Both methods take an optional timeout for acquiring the lock, and they poll DynamoDB at an interval you configure for that amount of time before giving up. The Groovy example in the README shows the compact form, polling every second for 5 additional seconds beyond the lease duration period:

groovy
LockItem lock = lockClient.acquireLock("Moe", "Test Data", 1, 5, TimeUnit.SECONDS);

Then there is the rule that decides how your latency budget is spent: acquireLock and tryAcquireLock will always poll DynamoDB for at least the leaseDuration period before giving up, unless shouldSkipBlockingWait is true, because this is the only way it will be able to expire stale locks.

That is the design's sharpest edge, and it is deliberate. A lock held by a dead process cannot be distinguished from a live one until the lease expires, so the client has to wait out the lease before it can say the lock is stale. Choosing shouldSkipBlockingWait trades correctness of the stale detection for a fast failure, and shouldSkipBlockingWait is exactly what the non-blocking example in the README uses when a caller has many messages and a limited set of processors.

Leader election is the use case that needs no extra machinery

The second use case in the README is leader election, and it is the one that fits this library most naturally.

The requirement is simple: if you only want one host to be the leader, this lock client is a way to pick one. The failover behaviour is described through the same lease mechanism: when the leader fails, it fails over to another host within a customizable leaseDuration that you set.

Nothing else is required. There is no separate election service, no membership protocol to implement and no separate table of candidates. A host attempts to acquire the lock on a fixed name, and the host that wins is the leader. When it dies, the lease expires and another host's acquisition succeeds.

The design also explains the coarse-grained mode. One name is a global lock, which is what a single-leader system needs, and arbitrary key names give you one lock per resource when the work is partitioned.

Two operational notes follow from the mechanism rather than from anything the README says explicitly. The failover time is the lease duration, so a leader election with a 10 second lease takes up to roughly 10 seconds to move after a crash, and the candidate hosts must all be polling for the same key on an interval that is short relative to the lease. Choose a lease longer than the interval by a wide margin or the election becomes noisy.

The table needs a hash key called key

There is one structural requirement on your side, and the README is precise about it: you need to set up a DynamoDB table that has a hash key on a key with the name key.

Two ways to create it are offered. There is a static method in the AmazonDynamoDBLockClient class called createLockTableInDynamoDB that you can use to set up your table, and it is also possible to set up the table in the AWS Console. Whichever you choose, the README is emphatic about timing: the table should be created in advance, since it takes a couple minutes for DynamoDB to provision your table.

The IAM side is spelled out as well. Acquiring or releasing a lock needs dynamodb:DeleteItem, dynamodb:GetItem, dynamodb:PutItem, dynamodb:Scan and dynamodb:UpdateItem. If you also create the table using createLockTableInDynamoDB, you need dynamodb:CreateTable. If you want to ensure a table exists using assertLockTableExists or lockTableExists, dynamodb:DescribeTable is necessary as well.

The presence of dynamodb:Scan in the acquire and release list is the one requirement that is worth thinking about. A scan on a large table is an expensive operation, and the list of permissions is what a lock client needs to find candidate lock records rather than read them by key. That is a design consequence of letting keys be arbitrary strings, and it is worth checking against your table's size and your account's scan limits before you put this on a hot path.

Adding the dependency and wiring the client

Using the library starts with a Maven dependency on the artifact, declared in your pom.xml:

xml
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>dynamodb-lock-client</artifactId>
    <version>1.5.0</version>
</dependency>

The client itself is built from a DynamoDbClient and an options object, and the example in the README shows both halves of the configuration, including the endpoint override that lets the same code run against a local DynamoDB on port 4567.

java
        final AmazonDynamoDBLockClient client = new AmazonDynamoDBLockClient(
            AmazonDynamoDBLockClientOptions.builder(dynamoDB, "lockTable")
                    .withTimeUnit(TimeUnit.SECONDS)
                    .withLeaseDuration(10L)
                    .withHeartbeatPeriod(3L)
                    .withCreateHeartbeatBackgroundThread(createHeartbeatBackgroundThread)
                    .build());

The options builder takes the DynamoDB client and the table name, then the unit for durations, the lease duration, the heartbeat period and the flag that decides whether the heartbeat thread exists at all. The acquisition call is the same shape: tryAcquireLock with AcquireLockOptions.builder and the lock name, and the result is an Optional of a LockItem that you release with releaseLock.

One more piece of the README is worth knowing before you write code against this. It states that the AmazonDynamoDBLockClient has JavaDoc comments that fully explain how the library works, so the detail on options, exceptions and edge cases lives in the API documentation rather than in this page.

Apache 2.0, community support, and no releases on GitHub

The licence is Apache 2.0, with a LICENSE.txt and a NOTICE.txt at the repository root, alongside a CODE_OF_CONDUCT.md and a CONTRIBUTING.md. That is a permissive grant, which is what you would expect for a library published under a com.amazonaws groupId and intended for use inside commercial systems.

On project health, the README is explicit rather than flattering. It states that DynamoDB Lock Client is an open-source project that will be supported by the community, and directs questions to the GitHub repository as issues. There is no support commitment, no SLA and no maintainer roster in the documentation.

The release record is the other thing to check. The repository has no GitHub releases, even though the README instructs you to declare a dependency on the latest version of the artifact and names 1.5.0 as the version to use. So the tagged artifacts live in a Maven repository rather than on the GitHub releases page, and the GitHub repository is where the source and the issues live. For an evaluator that means two things to confirm: which artifact repository your build resolves from, and which version of that artifact corresponds to the source tree you read.

The build itself runs on Travis against the master branch, as the badge in the README shows, and the tree is a Maven project with pom.xml and src/ at the root. On activity, the last push was on 2026-06-29.

Editorial conclusion

amazon-dynamodb-lock-client fits a Java service already provisioned on DynamoDB that needs one host to act as leader, or that must guarantee two workers never process the same customer or campaign at once, and that can accept DynamoDB as the coordination substrate. It does not fit a request path where a lock acquisition taking longer than one lease duration is unacceptable. Verify first that your table uses the hash key name key, that your IAM policy grants the five item permissions the README lists, and that your lease duration is longer than the worst-case time your holder needs the lock.

Frequently asked questions

What does amazon-dynamodb-lock-client provide?

It is a general purpose distributed locking library built for DynamoDB, supporting both fine-grained and coarse-grained locking because the lock keys can be any arbitrary string up to a certain length.

What DynamoDB table does the lock client need?

A table with a hash key on a key named key. You can create it with the static method createLockTableInDynamoDB or in the AWS Console, and the README says to create it in advance because DynamoDB takes a couple of minutes to provision it.

Which IAM permissions does the lock client require?

Acquiring and releasing locks needs dynamodb:DeleteItem, dynamodb:GetItem, dynamodb:PutItem, dynamodb:Scan and dynamodb:UpdateItem. Creating the table with createLockTableInDynamoDB adds dynamodb:CreateTable, and assertLockTableExists or lockTableExists need dynamodb:DescribeTable.

What is the difference between acquireLock and tryAcquireLock?

tryAcquireLock returns Optional.absent() when the lock was not acquired, while acquireLock throws a LockNotGrantedException. With shouldSkipBlockingWait set, acquireLock can throw a LockCurrentlyUnavailableException if the client did not wait long enough to tell whether the lock is stale, and tryAcquireLock catches both exceptions and returns Optional.absent().

How do heartbeats keep a lock alive?

With createHeartbeatBackgroundThread set to true, the client spawns a background thread that continually updates the record version number on the lock so it does not expire. As long as the JVM is running, the lock is held until releaseLock or lockItem.close is called, and a lease duration bounds how long the lock survives a crash.

How is amazon-dynamodb-lock-client licensed and released?

It is under Apache 2.0, with LICENSE.txt and NOTICE.txt in the repository root, and the project states that it is supported by the community. The repository has no GitHub releases, and the README tells you to depend on the latest artifact version, naming 1.5.0.

Official sources

  1. awslabs/amazon-dynamodb-lock-client on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/awslabs-amazon-dynamodb-lock-client.svg)](https://hysenlabs.com/projects/awslabs-amazon-dynamodb-lock-client)

Was this useful?