The asyncio redlock algorithm implementation.
The redlock algorithm is a distributed lock implementation for Redis. There are many implementations of it in several languages. In this case, this is the asyncio compatible implementation for python 3.5+.
from aioredlock import Aioredlock, LockError, Sentinel
# Define a list of connections to your Redis instances:
redis_instances = [
('localhost', 6379),
{'host': 'localhost', 'port': 6379, 'db': 1},
'redis://localhost:6379/2',
Sentinel(('localhost', 26379), master='leader', db=3),
Sentinel('redis://localhost:26379/4?master=leader&encoding=utf-8'),
Sentinel('rediss://:password@localhost:26379/5?master=leader&encoding=utf-8&ssl_cert_reqs=CERT_NONE'),
]
# Create a lock manager:
lock_manager = Aioredlock(redis_instances)
# Check wether a resourece acquired by any other redlock instance:
assert not await lock_manager.is_locked("resource_name")
# Try to acquire the lock:
try:
lock = await lock_manager.lock("resource_name", lock_timeout=10)
except LockError:
print('Lock not acquired')
raise
# Now the lock is acquired:
assert lock.valid
assert await lock_manager.is_locked("resource_name")
# Extend lifetime of the lock:
await lock_manager.extend(lock, lock_timeout=10)
# Raises LockError if the lock manager can not extend the lock lifetime
# on more then half of the Redis instances.
# Release the lock:
await lock_manager.unlock(lock)
# Raises LockError if the lock manager can not release the lock
# on more then half of redis instances.
# The released lock become invalid:
assert not lock.valid
assert not await lock_manager.is_locked("resource_name")
# Or you can use the lock as async context manager:
try:
async with await lock_manager.lock("resource_name") as lock:
assert lock.valid is True
# Do your stuff having the lock
await lock.extend() # alias for lock_manager.extend(lock)
# Do more stuff having the lock
assert lock.valid is False # lock will be released by context manager
except LockError:
print('Lock not acquired')
raise
# Clear the connections with Redis:
await lock_manager.destroy()
The Aioredlock constructor accepts the following optional parameters:
redis_connections
: A list of connections (dictionary of host and port and kwargs foraioredis.create_redis_pool()
, or tuple(host, port)
, or string Redis URI) where the Redis instances are running. The default value is[{'host': 'localhost', 'port': 6379}]
.retry_count
: An integer representing number of maximum allowed retries to acquire the lock. The default value is3
times.retry_delay_min
andretry_delay_max
: Float values representing waiting time (in seconds) before the next retry attempt. The default values are0.1
and0.3
, respectively.
In order to acquire the lock, the lock
function should be called. If the lock operation is successful, lock.valid
will be true, if the lock is not acquired then the LockError
will be raised.
From that moment, the lock is valid until the unlock
function is called or when the lock_timeout
is reached.
Call the extend
function to reset lifetime of the lock to lock_timeout
interval.
Use the is_locked
function to check if the resource is locked by other redlock instance.
In order to clear all the connections with Redis, the lock_manager destroy
method can be called.