Package: dynamo-dlm
dynamo-dlmDistributed lock manager for Python using AWS DynamoDB for persistenceCurrently this module exposes a single distributed locking primitive,DynamoDbLockthat functions similarly tothreading.Lockin the standard libraryLocks are scoped to a logical resource, represented by an arbitrary but uniquely identifying string, referred to below as theresource_id.
All instances ofDynamoDbLockwith the same table name and resource id will respect the lock rules.
By default, the lock looks for a DynamoDB table nameddynamo_dlm_locks.
You may use a custom name for the table as outlined below.
The table must have a primary key of typeStringnamedresource_idand NO sort key:Your application will need the following permissions in order to function properly:A note on capacity and performance:The lock class has been designed such that it should never fail under normal circumstances.
Given enough time, it should eventually acquire a lock even if there are thousands of simultaneous requests.
When using on-demand capacity all locks should acquire in constant time, every time.DynamoDB provisioned capacity is a topic too in depth to go into here but if you are using it and run into issues with locks taking too long to acquire then you likely need to increase your write capacity.
The lock shouldneverconsume read capacity. This is due to the way that DynamoDB handles conditional writes.
Logging has been added at log levelWARNINGto notify you of any backoffs related to provisioned capacity.When not constrained by write capacity or network speed, the average acquire/release cycle takes approximately 100ms when running outside of AWS.
If your execution environment is within AWS it will be markedly faster, as low as 10 ms when within the same region.Installation and usageInstall via pip:pip install dynamo-dlmUsing the lock primitive is pretty straightforward.
Just create an instance with a unique identifier and callacquire().
This will block any other instances' calls toacquire()untilrelease()is called or the lock expires, whichever comes first.
When you're done with the resource you needed locked, callrelease()to give up control of the lock.
Remember: all instances with the same identifier and table name will respect the same lock.
Ideally you want to time the expiration to slightly longer than you expect the operation to take.
The lock only operates on whole second increments, so the shortest reasonable lock time is 1 second.
As you're developing, keep an eye on theWARNINGlogger for indications that your locks are expiring, meaning operations are taking longer than expected.importdynamo_dlmasdlmresource_id='a unique resource identifier'lock=dlm.DynamoDbLock(resource_id)lock.acquire()# Code executed here is protected by the lock until it expireslock.release()The lock class is also implemented as a context manager:importdynamo_dlmasdlmresource_id='a unique resource identifier'withdlm.DynamoDbLock(resource_id):# Code executed inside the "with" block is protected by the lock until it expirespassBy default, locks last for 10 seconds if not released.
The duration and/or table name can be set globally at the module level:importdynamo_dlmasdlmdlm.DEFAULT_DURATION=5dlm.DEFAULT_TABLE_NAME='my_dynamo_db_lock_table'resource_id='a unique resource identifier'lock=dlm.DynamoDbLock(resource_id)They can also be overridden per lock:importdynamo_dlmasdlmresource_id='a unique resource identifier'lock=dlm.DynamoDbLock(resource_id,duration=5,table_name='my_dynamo_db_lock_table')