{"id":13621868,"url":"https://github.com/awslabs/amazon-dynamodb-lock-client","last_synced_at":"2025-04-15T05:32:37.912Z","repository":{"id":39759224,"uuid":"88083704","full_name":"awslabs/amazon-dynamodb-lock-client","owner":"awslabs","description":"The AmazonDynamoDBLockClient is a general purpose distributed locking library built on top of DynamoDB. It supports both coarse-grained and fine-grained locking.","archived":false,"fork":false,"pushed_at":"2024-10-10T17:01:28.000Z","size":228,"stargazers_count":486,"open_issues_count":54,"forks_count":91,"subscribers_count":28,"default_branch":"master","last_synced_at":"2025-03-13T02:37:39.603Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/awslabs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-04-12T18:27:48.000Z","updated_at":"2025-03-03T13:28:47.000Z","dependencies_parsed_at":"2024-01-14T08:18:05.728Z","dependency_job_id":"5ee4e1ee-9890-4d35-8f43-fd2bb698def2","html_url":"https://github.com/awslabs/amazon-dynamodb-lock-client","commit_stats":null,"previous_names":["awslabs/dynamodb-lock-client"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awslabs%2Famazon-dynamodb-lock-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awslabs%2Famazon-dynamodb-lock-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awslabs%2Famazon-dynamodb-lock-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awslabs%2Famazon-dynamodb-lock-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/awslabs","download_url":"https://codeload.github.com/awslabs/amazon-dynamodb-lock-client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249015771,"owners_count":21198823,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T21:01:11.421Z","updated_at":"2025-04-15T05:32:37.644Z","avatar_url":"https://github.com/awslabs.png","language":"Java","funding_links":[],"categories":["Java","分布式开发"],"sub_categories":[],"readme":"# Amazon DynamoDB Lock Client\n\nThe Amazon DynamoDB Lock Client is a general purpose distributed locking library\nbuilt for DynamoDB. The DynamoDB Lock Client supports both fine-grained and\ncoarse-grained locking as the lock keys can be any arbitrary string, up to a\ncertain length. DynamoDB Lock Client is an open-source project that will be\nsupported by the community. Please create issues in the GitHub repository with\nquestions.\n\n[![Build Status](https://travis-ci.org/awslabs/dynamodb-lock-client.svg?branch=master)](https://travis-ci.org/awslabs/dynamodb-lock-client)\n\n## Use cases\nA common use case for this lock client is:\nlet's say you have a distributed system that needs to periodically do work on a given campaign\n(or a given customer, or any other object) and you want to make sure that two boxes don't work\non the same campaign/customer at the same time. An easy way to fix this is to write a system that takes\na lock on a customer, but fine-grained locking is a tough problem. This library attempts to simplify\nthis locking problem on top of DynamoDB.\n\nAnother use case is leader election. If you only want one host to be the leader, then this lock\nclient is a great way to pick one. When the leader fails, it will fail over to another host\nwithin a customizable leaseDuration that you set.\n\n## Getting Started\nTo use the Amazon DynamoDB Lock Client, declare a dependency on the latest version of\nthis artifact in Maven in your pom.xml.\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.amazonaws\u003c/groupId\u003e\n    \u003cartifactId\u003edynamodb-lock-client\u003c/artifactId\u003e\n    \u003cversion\u003e1.3.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nThen, you need to set up a DynamoDB table that has a hash key on a key with the name `key`.\nFor your convenience, there is a static method in the AmazonDynamoDBLockClient class called\n`createLockTableInDynamoDB` that you can use to set up your table, but it is also possible to set\nup the table in the AWS Console. The table should be created in advance, since it takes a couple minutes\nfor DynamoDB to provision your table for you. The AmazonDynamoDBLockClient has JavaDoc comments that fully\nexplain how the library works. Here is some example code to get you started:\n\n```java\nimport java.io.IOException;\nimport java.net.URI;\nimport java.util.Optional;\nimport java.util.concurrent.TimeUnit;\nimport org.junit.Test;\nimport software.amazon.awssdk.regions.Region;\nimport software.amazon.awssdk.services.dynamodb.DynamoDbClient;\n\npublic class LockClientExample {\n    @Test\n    public void usageExample() throws InterruptedException, IOException {\n        // Inject client configuration to the builder like the endpoint and signing region\n        final DynamoDbClient dynamoDB = DynamoDbClient.builder()\n                .region(Region.US_WEST_2).endpointOverride(URI.create(\"http://localhost:4567\"))\n                .build();\n        // Whether or not to create a heartbeating background thread\n        final boolean createHeartbeatBackgroundThread = true;\n        //build the lock client\n        final AmazonDynamoDBLockClient client = new AmazonDynamoDBLockClient(\n            AmazonDynamoDBLockClientOptions.builder(dynamoDB, \"lockTable\")\n                    .withTimeUnit(TimeUnit.SECONDS)\n                    .withLeaseDuration(10L)\n                    .withHeartbeatPeriod(3L)\n                    .withCreateHeartbeatBackgroundThread(createHeartbeatBackgroundThread)\n                    .build());\n        //try to acquire a lock on the partition key \"Moe\"\n        final Optional\u003cLockItem\u003e lockItem =\n                client.tryAcquireLock(AcquireLockOptions.builder(\"Moe\").build());\n        if (lockItem.isPresent()) {\n            System.out.println(\"Acquired lock! If I die, my lock will expire in 10 seconds.\");\n            System.out.println(\"Otherwise, I will hold it until I stop heartbeating.\");\n            client.releaseLock(lockItem.get());\n        } else {\n            System.out.println(\"Failed to acquire lock!\");\n        }\n        client.close();\n    }\n}\n```\n\n#### Permissions\nThe following permissions are required in order to use the AmazonDynamoDBLockClient to acquire or release a lock:\n- `dynamodb:DeleteItem`\n- `dynamodb:GetItem`\n- `dynamodb:PutItem`\n- `dynamodb:Scan`\n- `dynamodb:UpdateItem`\n\nIf you also create a table using `AmazonDynamoDBLockClient.createLockTableInDynamoDB`, you need these permissions:\n- `dynamodb:CreateTable`\n\nIf you want to ensure a table exists using `AmazonDynamoDBLockClient.assertLockTableExists` or\n`AmazonDynamoDBLockClient.lockTableExists`, the following permissions are necessary as well:\n- `dynamodb:DescribeTable`\n\n\n## Selected Features\n### Send Automatic Heartbeats\nWhen you call the constructor AmazonDynamoDBLockClient, you can specify `createHeartbeatBackgroundThread=true`\nlike in the above example, and it will spawn a background thread that continually updates the record version\nnumber on your locks to prevent them from expiring (it does this by calling the sendHeartbeat() method in the\nlock client.) This will ensure that as long as your JVM is running, your locks will not expire until you call\nreleaseLock() or lockItem.close()\n\n### Acquire lock with timeout\nYou can acquire a lock via two different methods: acquireLock or tryAcquireLock. The difference between the\ntwo methods is that tryAcquireLock will return Optional.absent() if the lock was not acquired, whereas\nacquireLock will throw a LockNotGrantedException. Both methods provide optional parameters where you can specify\nan additional timeout for acquiring the lock. Then they will try to acquire the lock for that amount of time\nbefore giving up. They do this by continually polling DynamoDB according to an interval you set up. Remember that\nacquireLock/tryAcquireLock will always poll DynamoDB for at least the leaseDuration period before giving up,\nbecause this is the only way it will be able to expire stale locks.\n\nThis example will poll DynamoDB every second for 5 additional seconds (beyond the lease duration period),\ntrying to acquire a lock:\n```groovy\nLockItem lock = lockClient.acquireLock(\"Moe\", \"Test Data\", 1, 5, TimeUnit.SECONDS);\n```\n### Acquire lock without blocking the user thread.\nExample Use Case:\n Suppose you have many messages that need to be processed for multiple lockable entities by a limited set of\n processor-consumers. Further suppose that the processing time for each message is significant (for example, 15 minutes).\n You also need to prevent multiple processing for the same resource.\n\n```\n  @Test\n    public void acquireLockNonBlocking() throws LockAlreadyOwnedException {\n        AcquireLockOptions lockOptions = AcquireLockOptions.builder(\"partitionKey\")\n                                        .withShouldSkipBlockingWait(false).build();\n        LockItem lock = lockClient.acquireLock(lockOptions);\n    }\n```\nThe above implementation of the locking client, would try to acquire lock, waiting for at least the lease duration (15\nminutes in our case). If the lock is already being held by other worker. This essentially blocks the threads from being\nused to process other messages in the queue.\n\nSo we introduced an optional behavior which offers a Non-Blocking acquire lock implementation. While trying to acquire\nlock, the client can now optionally set `shouldSkipBlockingWait = true` to prevent the user thread from being\nblocked until the lease duration, if the lock has already been held by another worker and has not been released yet.\nThe caller can chose to immediately retry the lock acquisition or to back off and retry the lock acquisition, if lock is\ncurrently unavailable.\n\n```\n    @Test\n    public void acquireLockNonBlocking() throws LockAlreadyOwnedException {\n        AcquireLockOptions lockOptions = AcquireLockOptions.builder(\"partitionKey\")\n                                        .withShouldSkipBlockingWait(true).build();\n        LockItem lock = lockClient.acquireLock(lockOptions);\n    }\n```\nIf the lock does not exist or if the lock has been acquired by the other machine and is stale (has passed the lease\nduration), this would successfully acquire the lock.\n\nIf the lock has already been held by another worker and has not been released yet and the lease duration has not expired\nsince the lock was last updated by the current owner, this will throw a LockCurrentlyUnavailableException exception.\nThe caller can chose to immediately retry the lock acquisition or to delay the processing for that lock item by NACKing\nthe message.\n\n### Read the data in a lock without acquiring it\nYou can read the data in the lock without acquiring it, and find out who owns the lock. Here's how:\n```groovy\nLockItem lock = lockClient.getLock(\"Moe\");\n```\n\n## How we handle clock skew\nThe lock client never stores absolute times in DynamoDB -- only the relative \"lease duration\" time is stored\nin DynamoDB. The way locks are expired is that a call to acquireLock reads in the current lock, checks the\nRecordVersionNumber of the lock (which is a GUID) and starts a timer. If the lock still has the same GUID after\nthe lease duration time has passed, the client will determine that the lock is stale and expire it.\nWhat this means is that, even if two different machines disagree about what time it is, they will still avoid\nclobbering each other's locks.\n\n## Testing the DynamoDB Locking client\nTo run all integration tests for the DynamoDB Lock client, issue the following Maven command:\n\n```bash\nmvn clean install -Pintegration-tests\n```\n\n# External release to Sonatype\n```bash\nmvn deploy -Possrh-release\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawslabs%2Famazon-dynamodb-lock-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fawslabs%2Famazon-dynamodb-lock-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawslabs%2Famazon-dynamodb-lock-client/lists"}