Lock Module

This module provides distributed locking mechanisms using Redis as the backend.

Overview

The lock module implements distributed locking patterns for coordinating access to shared resources in a distributed environment. It offers two primary implementations:

  1. Redis-based locks (RedisLock, RedisLockPool): Distributed locks using Redis

  2. Thread-local locks (ThreadLock, ThreadLockPool): In-memory locks for single-process applications

Key Features

  • Automatic Expiry: Locks automatically expire after a configured timeout

  • Active Update: Lock owners must periodically update locks to maintain ownership

  • Reentrant Locks: Same owner can re-acquire its own locks with rlock

  • Lock Pools: Manage collections of locks for allocation and tracking

Implementation Notes

  • Redis-based locks require a single Redis instance (not compatible with Redis Cluster)

  • Locks have a timeout to prevent deadlocks in case of client failures

  • Thread identification is used to determine lock ownership

RedisLock

class redis_allocator.lock.RedisLock[source]

Redis-based lock implementation.

Uses standard Redis commands (SET with NX, EX options) for basic locking and Lua scripts for conditional operations (set/del based on value comparison).

redis

StrictRedis client instance (must decode responses).

Type:

redis.client.Redis

prefix

Prefix for all Redis keys managed by this lock instance.

Type:

str

suffix

Suffix for Redis keys to distinguish lock types (e.g., ‘lock’).

Type:

str

eps

Epsilon for float comparisons in conditional Lua scripts.

Type:

float

__init__(redis, prefix, suffix='lock', eps=1e-06)[source]

Initialize a RedisLock instance.

Parameters:
  • redis (Redis) – Redis client instance.

  • prefix (str) – Prefix for Redis keys.

  • suffix – Suffix for Redis keys.

  • eps (float) – Epsilon value for floating point comparison.

redis: Redis
prefix: str
suffix: str
key_status(key, timeout=120)[source]

Get the status of a key.

Parameters:
  • key (str) – The key to check the status of.

  • timeout (int) – The lock timeout in seconds.

Returns:

The current status of the key.

Return type:

LockStatus

update(key, value='1', timeout=120)[source]

Lock a key for a specified duration without checking if the key is already locked.

Parameters:
  • key (str) – The key to lock.

  • value – The value to set for the key.

  • timeout (float | timedelta | None) – The lock timeout in seconds.

lock(key, value='1', timeout=120)[source]

Try to lock a key for a specified duration.

Parameters:
  • key (str) – The key to lock.

  • value (str) – The value to set for the key.

  • timeout (float | timedelta | None) – The lock timeout in seconds.

Returns:

True if the ownership of the key is successfully acquired, False otherwise.

Return type:

bool

is_locked(key)[source]

Check if a key is locked.

Parameters:

key (str) – The key to check.

Returns:

True if the key is locked, False otherwise.

Return type:

bool

lock_value(key)[source]

Get the value of a locked key.

Parameters:

key (str) – The key to get the value of.

Returns:

The value of the key if the key is locked, None otherwise.

Return type:

str | None

rlock(key, value='1', timeout=120)[source]

Try to lock a key for a specified duration.

When the value is the same as the current value, the function will return True.

Parameters:
  • key (str) – The key to lock.

  • value (str) – The value to set for the key.

  • timeout – The lock timeout in seconds.

Returns:

True if the ownership of the key is successfully acquired, False otherwise.

Return type:

bool

unlock(key)[source]

Forcefully release a key without checking if the key is locked.

Parameters:

key (str) – The key to release.

Returns:

True if the key is successfully released, False if the key is not locked.

Return type:

bool

deleq(key, value)

Deletes a key when the comparison value is equal to the current value.

Parameters:
delge(key, value)

Deletes a key when the comparison value is greater than or equal to the current value.

Parameters:
delgt(key, value)

Deletes a key when the comparison value is greater than the current value.

Parameters:
delle(key, value)

Deletes a key when the comparison value is less than or equal to the current value.

Parameters:
dellt(key, value)

Deletes a key when the comparison value is less than the current value.

Parameters:
delne(key, value)

Deletes a key when the comparison value is not equal to the current value.

Parameters:
seteq(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is equal to the current value.

Parameters:
Return type:

bool

setge(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is greater than or equal to the current value.

Parameters:
Return type:

bool

setgt(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is greater than the current value.

Parameters:
Return type:

bool

setle(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is less than or equal to the current value.

Parameters:
Return type:

bool

setlt(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is less than the current value.

Parameters:
Return type:

bool

setne(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is not equal to the current value.

Parameters:
Return type:

bool

eps: float

RedisLockPool

class redis_allocator.lock.RedisLockPool[source]

Manages a collection of RedisLock keys as a logical pool.

Uses a Redis Set (<prefix>|<suffix>|pool) to store the identifiers (keys) belonging to the pool. Inherits locking logic from RedisLock.

Provides methods to add (extend), remove (shrink), replace (assign), and query (keys, __contains__) the members of the pool. Also offers methods to check the lock status of pool members (_get_key_lock_status).

__init__(redis, prefix, suffix='lock-pool', eps=1e-06)[source]

Initialize a RedisLockPool instance.

Parameters:
  • redis (Redis) – Redis client instance.

  • prefix (str) – Prefix for Redis keys.

  • suffix – Suffix for Redis keys.

  • eps (float) – Epsilon value for floating point comparison.

extend(keys=None)[source]

Extend the pool with the specified keys.

Parameters:

keys (Sequence[str] | None)

shrink(keys)[source]

Shrink the pool by removing the specified keys.

Parameters:

keys (Sequence[str])

assign(keys=None)[source]

Assign keys to the pool, replacing any existing keys.

Parameters:

keys (Sequence[str] | None)

clear()[source]

Empty the pool.

keys()[source]

Get the keys in the pool.

Return type:

Iterable[str]

__contains__(key)[source]

Check if a key is in the pool.

__iter__()

Iterate over the keys in the pool.

__len__()

Get the number of keys in the pool.

deleq(key, value)

Deletes a key when the comparison value is equal to the current value.

Parameters:
delge(key, value)

Deletes a key when the comparison value is greater than or equal to the current value.

Parameters:
delgt(key, value)

Deletes a key when the comparison value is greater than the current value.

Parameters:
delle(key, value)

Deletes a key when the comparison value is less than or equal to the current value.

Parameters:
dellt(key, value)

Deletes a key when the comparison value is less than the current value.

Parameters:
delne(key, value)

Deletes a key when the comparison value is not equal to the current value.

Parameters:
health_check()

Check the health status of the keys in the pool.

Returns:

A tuple of (locked_count, free_count)

Return type:

Tuple[int, int]

is_locked(key)

Check if a key is locked.

Parameters:

key (str) – The key to check.

Returns:

True if the key is locked, False otherwise.

Return type:

bool

items_locked_status()

Get (key, lock_status) pairs for all keys in the pool.

Return type:

Iterable[Tuple[str, bool]]

key_status(key, timeout=120)

Get the status of a key.

Parameters:
  • key (str) – The key to check the status of.

  • timeout (int) – The lock timeout in seconds.

Returns:

The current status of the key.

Return type:

LockStatus

lock(key, value='1', timeout=120)

Try to lock a key for a specified duration.

Parameters:
  • key (str) – The key to lock.

  • value (str) – The value to set for the key.

  • timeout (float | timedelta | None) – The lock timeout in seconds.

Returns:

True if the ownership of the key is successfully acquired, False otherwise.

Return type:

bool

lock_value(key)

Get the value of a locked key.

Parameters:

key (str) – The key to get the value of.

Returns:

The value of the key if the key is locked, None otherwise.

Return type:

str | None

rlock(key, value='1', timeout=120)

Try to lock a key for a specified duration.

When the value is the same as the current value, the function will return True.

Parameters:
  • key (str) – The key to lock.

  • value (str) – The value to set for the key.

  • timeout – The lock timeout in seconds.

Returns:

True if the ownership of the key is successfully acquired, False otherwise.

Return type:

bool

seteq(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is equal to the current value.

Parameters:
Return type:

bool

setge(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is greater than or equal to the current value.

Parameters:
Return type:

bool

setgt(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is greater than the current value.

Parameters:
Return type:

bool

setle(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is less than or equal to the current value.

Parameters:
Return type:

bool

setlt(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is less than the current value.

Parameters:
Return type:

bool

setne(key, value, set_value=None, ex=None)

Sets a new value when the comparison value is not equal to the current value.

Parameters:
Return type:

bool

unlock(key)

Forcefully release a key without checking if the key is locked.

Parameters:

key (str) – The key to release.

Returns:

True if the key is successfully released, False if the key is not locked.

Return type:

bool

update(key, value='1', timeout=120)

Lock a key for a specified duration without checking if the key is already locked.

Parameters:
  • key (str) – The key to lock.

  • value – The value to set for the key.

  • timeout (float | timedelta | None) – The lock timeout in seconds.

values_lock_status()

Get the lock status of all keys in the pool.

Return type:

Iterable[bool]

redis: Redis
prefix: str
suffix: str
eps: float

LockStatus

class redis_allocator.lock.LockStatus[source]

Enumeration representing the status of a Redis lock.

The LockStatus enum defines the possible states of a Redis lock:

  • FREE: The lock is not being used.

  • UNAVAILABLE: The lock is being used by another program, or it has been marked as unavailable for a certain period of time.

  • LOCKED: The lock is being used by the current program.

  • ERROR: The lock is being used permanently, indicating a potential issue with the program.

FREE = 0
UNAVAILABLE = 1
LOCKED = 2
ERROR = 4
__new__(value)

Base Classes

class redis_allocator.lock.BaseLock[source]

Abstract base class defining the interface for lock implementations.

eps

Epsilon value for floating point comparison.

Type:

float

__init__(eps=1e-06)[source]

Initialize a BaseLock instance.

Parameters:

eps (float) – Epsilon value for floating point comparison.

eps: float
abstract key_status(key, timeout=120)[source]

Get the status of a key.

Parameters:
  • key (str) – The key to check the status of.

  • timeout (int) – The lock timeout in seconds.

Returns:

The current status of the key.

Return type:

LockStatus

abstract update(key, value='1', timeout=120)[source]

Lock a key for a specified duration without checking if the key is already locked.

Parameters:
  • key (str) – The key to lock.

  • value – The value to set for the key.

  • timeout (float | timedelta | None) – The lock timeout in seconds.

abstract lock(key, value='1', timeout=120)[source]

Try to lock a key for a specified duration.

Parameters:
  • key (str) – The key to lock.

  • value (str) – The value to set for the key.

  • timeout (float | timedelta | None) – The lock timeout in seconds.

Returns:

True if the ownership of the key is successfully acquired, False otherwise.

Return type:

bool

abstract is_locked(key)[source]

Check if a key is locked.

Parameters:

key (str) – The key to check.

Returns:

True if the key is locked, False otherwise.

Return type:

bool

abstract lock_value(key)[source]

Get the value of a locked key.

Parameters:

key (str) – The key to get the value of.

Returns:

The value of the key if the key is locked, None otherwise.

Return type:

str | None

abstract rlock(key, value='1', timeout=120)[source]

Try to lock a key for a specified duration.

When the value is the same as the current value, the function will return True.

Parameters:
  • key (str) – The key to lock.

  • value (str) – The value to set for the key.

  • timeout – The lock timeout in seconds.

Returns:

True if the ownership of the key is successfully acquired, False otherwise.

Return type:

bool

abstract unlock(key)[source]

Forcefully release a key without checking if the key is locked.

Parameters:

key (str) – The key to release.

Returns:

True if the key is successfully released, False if the key is not locked.

Return type:

bool

setgt(key, value, set_value=None, ex=None)[source]

Sets a new value when the comparison value is greater than the current value.

Parameters:
Return type:

bool

setlt(key, value, set_value=None, ex=None)[source]

Sets a new value when the comparison value is less than the current value.

Parameters:
Return type:

bool

setge(key, value, set_value=None, ex=None)[source]

Sets a new value when the comparison value is greater than or equal to the current value.

Parameters:
Return type:

bool

setle(key, value, set_value=None, ex=None)[source]

Sets a new value when the comparison value is less than or equal to the current value.

Parameters:
Return type:

bool

seteq(key, value, set_value=None, ex=None)[source]

Sets a new value when the comparison value is equal to the current value.

Parameters:
Return type:

bool

setne(key, value, set_value=None, ex=None)[source]

Sets a new value when the comparison value is not equal to the current value.

Parameters:
Return type:

bool

delgt(key, value)[source]

Deletes a key when the comparison value is greater than the current value.

Parameters:
dellt(key, value)[source]

Deletes a key when the comparison value is less than the current value.

Parameters:
delge(key, value)[source]

Deletes a key when the comparison value is greater than or equal to the current value.

Parameters:
delle(key, value)[source]

Deletes a key when the comparison value is less than or equal to the current value.

Parameters:
deleq(key, value)[source]

Deletes a key when the comparison value is equal to the current value.

Parameters:
delne(key, value)[source]

Deletes a key when the comparison value is not equal to the current value.

Parameters:
class redis_allocator.lock.BaseLockPool[source]

Abstract base class defining the interface for lock pool implementations.

A lock pool manages a collection of lock keys as a group, providing methods to track, add, remove, and check lock status of multiple keys.

eps

Epsilon value for floating point comparison.

Type:

float

abstract extend(keys=None)[source]

Extend the pool with the specified keys.

Parameters:

keys (Sequence[str] | None)

abstract shrink(keys)[source]

Shrink the pool by removing the specified keys.

Parameters:

keys (Sequence[str])

abstract assign(keys=None)[source]

Assign keys to the pool, replacing any existing keys.

Parameters:

keys (Sequence[str] | None)

abstract clear()[source]

Empty the pool.

abstract keys()[source]

Get the keys in the pool.

Return type:

Iterable[str]

values_lock_status()[source]

Get the lock status of all keys in the pool.

Return type:

Iterable[bool]

items_locked_status()[source]

Get (key, lock_status) pairs for all keys in the pool.

Return type:

Iterable[Tuple[str, bool]]

health_check()[source]

Check the health status of the keys in the pool.

Returns:

A tuple of (locked_count, free_count)

Return type:

Tuple[int, int]

__len__()[source]

Get the number of keys in the pool.

__iter__()[source]

Iterate over the keys in the pool.

Thread-Local Implementations

class redis_allocator.lock.ThreadLock[source]

In-memory, thread-safe lock implementation conforming to BaseLock.

Simulates Redis lock behavior using Python’s threading.RLock for concurrency control and a defaultdict to store lock data (value and expiry timestamp). Suitable for single-process scenarios or testing.

eps

Epsilon for float comparisons.

Type:

float

_locks

defaultdict storing LockData(value, expiry) for each key.

_lock

threading.RLock protecting access to _locks.

__init__(eps=1e-06)[source]

Initialize a ThreadLock instance.

Parameters:

eps (float) – Epsilon value for floating point comparison.

key_status(key, timeout=120)[source]

Get the status of a key.

Parameters:
Return type:

LockStatus

update(key, value='1', timeout=120)[source]

Lock a key for a specified duration without checking if already locked.

Parameters:
lock(key, value='1', timeout=120)[source]

Try to lock a key for a specified duration.

Parameters:
Return type:

bool

is_locked(key)[source]

Check if a key is locked.

Parameters:

key (str)

Return type:

bool

lock_value(key)[source]

Get the value of a locked key.

Parameters:

key (str)

Return type:

str | None

rlock(key, value='1', timeout=120)[source]

Try to relock a key for a specified duration.

Parameters:
Return type:

bool

unlock(key)[source]

Forcefully release a key.

Parameters:

key (str)

Return type:

bool

class redis_allocator.lock.ThreadLockPool[source]

In-memory, thread-safe lock pool implementation.

Manages a collection of lock keys using a Python set for the pool members and inherits the locking logic from ThreadLock.

_pool

Set containing the keys belonging to this pool.

_lock

threading.RLock protecting access to _locks and _pool.

__init__(eps=1e-06)[source]

Initialize a ThreadLockPool instance.

Parameters:

eps (float)

extend(keys=None)[source]

Extend the pool with the specified keys.

Parameters:

keys (Sequence[str] | None)

shrink(keys)[source]

Shrink the pool by removing the specified keys.

Parameters:

keys (Sequence[str])

assign(keys=None)[source]

Assign keys to the pool, replacing any existing keys.

Parameters:

keys (Sequence[str] | None)

clear()[source]

Empty the pool.

keys()[source]

Get the keys in the pool.

Return type:

Iterable[str]

__contains__(key)[source]

Check if a key is in the pool.

Usage Patterns

Basic Lock Usage

lock = RedisLock(redis_client, "app", "lock")

# Acquire lock with thread ID as value and 60-second timeout
if lock.lock("resource-1", value=thread_id, timeout=60):
    try:
        # Resource is locked for 60 seconds
        # Process the resource...

        # Extend lock timeout by updating it
        lock.update("resource-1", value=thread_id, timeout=60)

        # Continue processing...
    finally:
        # Always release the lock when done
        lock.unlock("resource-1")

Reentrant Lock Usage

# First acquire the lock normally
if lock.lock("resource-1", value=thread_id, timeout=60):
    try:
        # Later, the same thread can re-acquire the lock
        if lock.rlock("resource-1", value=thread_id):
            # Process the resource again...
            pass
    finally:
        # Release the lock once
        lock.unlock("resource-1")

Using Lock Pools

pool = RedisLockPool(redis_client, "app", "pool")

# Add resources to the pool
pool.extend(["resource-1", "resource-2"])

# Lock a specific resource
if pool.lock("resource-1"):
    try:
        # Use the resource
        pass
    finally:
        # Release the lock
        pool.unlock("resource-1")

Simplified Lock Flow

This diagram illustrates the typical sequence for acquiring, updating, and releasing a lock using RedisLock.

        sequenceDiagram
    participant Client
    participant RedisLock
    participant Redis

    Client->>RedisLock: lock("key", "id", timeout=60)
    note right of Redis: Attempts SET key id NX EX 60
    RedisLock->>Redis: SET key id NX EX 60
    alt Lock Acquired
        Redis-->>RedisLock: OK
        RedisLock-->>Client: True
        Client->>RedisLock: update("key", "id", timeout=60)
        note right of Redis: Refreshes expiry: SET key id EX 60
        RedisLock->>Redis: SET key id EX 60
        Redis-->>RedisLock: OK
        Client->>RedisLock: unlock("key")
        note right of Redis: Removes lock: DEL key
        RedisLock->>Redis: DEL key
        Redis-->>RedisLock: 1 (deleted)
        RedisLock-->>Client: True
    else Lock Not Acquired (Already Locked)
        Redis-->>RedisLock: nil
        RedisLock-->>Client: False
    end