API¶
Locks¶
-
class
redlock_plus.Lock(resource_name, connection_details=None, nodes=None, retry_times=3, retry_delay=200, ttl=120000)¶ A distributed lock implementation based on Redis. It shares the same API as Python’s
threading.Lockand behaves mostly the same. Generally this can be used as a drop-in replacement, given the lock is provided from aLockFactory.The lock supports the context manager protocol, which is the preferred way to use it
with Lock("my_resource"): # do some work # this is equivalent to lock = Lock("my_resource") lock.acquire() # do some stuff lock.release()
- Parameters
resource_name (
str) – Global identifier to be used for the lock. This will be shared across all redis nodesconnection_details (
Optional[List[Dict[str,Any]]]) – A list containing either redis client instances or dicts that can be used to create a redis client. If None, nodes must not be Nonenodes (
Optional[List[Redis]]) – A list containing already initialised redis nodes. Takes precedence over connection_details. If None, connection_details must not be Noneretry_times (
int) – Amount of times to retry acquiring a lock after a failed attemptretry_delay (
int) – Time in milliseconds between retry attempts to acquire a lockttl (
int) – Time in seconds until the lock should expire. This should be set to a relatively high amount compared to the time it takes to complete the work for which the lock should be held. Default is 120_000 milliseconds (2 minutes)
-
start_autoextend(timeout=None)¶ Start an autoextending thread which will attempt to extedn the lock at 3/4 of its expected ttl.
- Parameters
timeout (
Optional[float]) – Timeout in seconds until the thread terminates. If None, the lock will be extended indefinitely (i.e. as long as the main thread is running)- Return type
- Returns
The autoextending thread
- Raises
InvalidOperationError – If the lock was not previously acquired
-
stop_autoextend()¶ Stop the autoextending thread
- Return type
None
-
acquire(blocking=True, timeout=- 1, autoextend=True, autoextend_timeout=None)¶ Attempt to acquire a new lock, blocking or non-blocking.
- Parameters
blocking (
bool) – If True, block until the lock can be acquiredtimeout (
float) – If blocking is True and timeout is a positive value, in the case a request would block, block at most timeout secondsautoextend (
bool) – If True start a thread once the lock is acquired that will attempt to extend the lock at 3/4 of its expected ttlautoextend_timeout (
Optional[float]) – Timeout in seconds after which the autoextend thread will terminate regardless of the lock status
- Return type
- Returns
A float indicating the minimal time the lock can be considered held in milliseconds in case the lock could be acquired, else False
- Raises
ValueError – If blocking is False and timeout is a positive value
-
extend()¶ Extend an acquired lock.
- Return type
- Returns
A float indicating the minimal time the lock can be considered held in milliseconds in case the lock could be acquired, else False
- Raises
InvalidOperationError – If the lock was not previously acquired
-
acquire_or_extend(blocking=True, timeout=- 1, autoextend=True, autoextend_timeout=None)¶ If the lock is currently not held, try to acquire it. If it is held, extend it. blocking, timeout, autoextend and autoextend_timeout will be passed to
Lock.acquire().- Return type
- Returns
A float indicating the minimal time the lock can be considered held in milliseconds in case the lock could be acquired, else False
-
check_times()¶ Check if the lock is still held and the time to live on each node, accounting for clock drift and request time.
- Return type
- Returns
A tuple consisting of a boolean, indicating if the lock can still be considered held and a list of the reported times in milliseconds of each node
- Raises
InvalidOperationError – If the lock was not previously acquired
-
release()¶ Release the lock. A lock is considered released if the action to release it could be performed sucessfully on the majority of redis nodes. So unlike Python’s
threading.Lockthis can fail for reasons other than the lock not being held in the first place.- Raises
InvalidOperationError – If the lock was not previously acquired
- Return type
- Returns
Whether or not the lock was successfully released
-
class
redlock_plus.RLock(*args, **kwargs)¶ A reentrant version of
Lock, the only difference being that calls to acquire / release may be nested. Only the final call to acquire actually releases the lock.-
acquire(blocking=True, timeout=- 1, autoextend=False, autoextend_timeout=None)¶ Attempt to acquire a new lock and / or increment the recursion level. If the recursion level was 0, acquire a lock, otherwise just increase the recursion level.
- Parameters
blocking (
bool) – If True, block until the lock can be acquiredtimeout (
float) – If blocking is True and timeout is a positive value, in the case a request would block, block at most timeout secondsautoextend (
bool) – If True start a thread once the lock is acquired that will attempt to extend the lock at 3/4 of its expected ttlautoextend_timeout (
Optional[float]) – Timeout in seconds after which the autoextend thread will terminate regardless of the lock status
- Return type
- Returns
A float indicating the minimal time the lock can be considered held in milliseconds in case the lock could be acquired, else False
- Raises
ValueError – If blocking is False and timeout is a positive value
RedlockError – The recursion level should be increased but the lock was lost in the meantime
-
acquire_or_extend(blocking=True, timeout=- 1, autoextend=True, autoextend_timeout=None)¶ If the lock is currently not held, try to acquire it. If it is held, extend it. blocking, timeout, autoextend and autoextend_timeout will be passed to
Lock.acquire(). If a lock was acquired or extended, increase the recursion level.- Return type
- Returns
A float indicating the minimal time the lock can be considered held in milliseconds in case the lock could be acquired, else False
-
release()¶ Release the lock and / or decrement the recursion level. If the recursion level reaches 0 release the lock. A lock is considered released if the action to release it could be performed sucessfully on the majority of redis nodes. So unlike Python’s
threading.RLockthis can fail for reasons other than the lock not being held in the first place.- Raises
InvalidOperationError – If the lock was not previously acquired
- Return type
- Returns
Whether or not the lock was successfully released
-
Helpers¶
-
class
redlock_plus.LockFactory(connection_details, lock_class=None, **kwargs)¶ Create new
Lockinstances from a fixed configuration.
-
class
redlock_plus.RLockFactory(connection_details, lock_class=None, **kwargs)¶ Convenience subclass of
LockFactory, to create RLocks.
Exceptions¶
-
class
redlock_plus.RedlockError¶ Base redlock exception
-
class
redlock_plus.InsufficientNodesError(node_count, *args)¶ Raised if the minimum amount of 3 nodes was not met
-
class
redlock_plus.InvalidOperationError¶ An operation was performed on the lock which the current state of the lock does not allow