Allocator Module
This module provides resource allocation mechanisms using Redis as the backend.
RedisAllocator
- class redis_allocator.allocator.RedisAllocator[source]
A Redis-based distributed allocation system.
Manages a pool of resource identifiers (keys) using Redis, allowing distributed clients to allocate, free, and manage these resources. It leverages Redis’s atomic operations via Lua scripts for safe concurrent access.
The allocator maintains a doubly-linked list in a Redis hash to track available (free) resources. Allocated resources are tracked using standard Redis keys that act as locks.
Key Concepts: - Allocation Pool: A set of resource identifiers (keys) managed by the allocator.
Stored in a Redis hash (<prefix>|<suffix>|pool) representing a doubly-linked list. Head/Tail pointers are stored in separate keys (<prefix>|<suffix>|pool|head, <prefix>|<suffix>|pool|tail).
Free List: The subset of keys within the pool that are currently available. Represented by the linked list structure within the pool hash.
Allocated State: A key is considered allocated if a corresponding lock key exists (<prefix>|<suffix>:<key>).
Shared Mode: If shared=True, allocating a key moves it to the tail of the free list but does not create a lock key. This allows multiple clients to “allocate” the same key concurrently, effectively using the list as a rotating pool. If shared=False (default), allocation creates a lock key, granting exclusive access.
Soft Binding: Allows associating a logical name with an allocated key. If an object provides a name, the allocator tries to reuse the previously bound key for that name. Stored in Redis keys like <prefix>|<suffix>-cache:bind:<name>.
Garbage Collection (GC): Periodically scans the pool to reconcile the free list with the lock states. Removes expired/locked items from the free list and returns items whose locks have expired back to the free list.
Policies: Uses RedisAllocatorPolicy (e.g., DefaultRedisAllocatorPolicy) to customize allocation behavior, GC triggering, and pool updates.
Generic type U should implement RedisAllocatableClass.
- __init__(redis, prefix, suffix='allocator', eps=1e-06, shared=False, policy=None)[source]
Initializes the RedisAllocator.
- Parameters:
redis (Redis) – StrictRedis client instance (must decode responses).
prefix (str) – Prefix for all Redis keys used by this allocator instance.
suffix – Suffix to uniquely identify this allocator instance’s keys.
eps – Small float tolerance for comparisons (used by underlying lock).
shared – If True, operates in shared mode (keys are rotated, not locked). If False (default), keys are locked upon allocation.
policy (RedisAllocatorPolicy[U] | None) – Optional allocation policy. Defaults to DefaultRedisAllocatorPolicy.
- extend(keys=None, timeout=-1)[source]
Add new resources to the allocation pool.
- Parameters:
- Raises:
ValueError – If any key fails
_validate_keys().
- assign(keys=None, timeout=-1)[source]
Completely replace the resources in the allocation pool.
- Parameters:
keys (Sequence[str] | None) – Sequence of resource identifiers to assign to the pool, replacing any existing resources. Each key is validated by
_validate_keys()before being inserted; invalid keys cause the entire call to be rejected (the existing pool is left untouched).timeout (int) – Optional timeout in seconds for the pool items (-1 means no timeout)
- Raises:
ValueError – If any key fails
_validate_keys().
- __contains__(key)[source]
Check if a resource identifier is in the allocation pool.
- Parameters:
key – Resource identifier to check
- Returns:
True if the resource is in the pool, False otherwise
- update_soft_bind(name, key, timeout=3600)[source]
Update a soft binding between a name and a resource.
Soft bindings create a persistent mapping between named objects and allocated keys, allowing the same key to be consistently allocated to the same named object. This is useful for maintaining affinity between objects and their resources.
- unbind_soft_bind(name)[source]
Remove a soft binding.
This removes the persistent mapping between a named object and its allocated key, allowing the key to be freely allocated to any requestor.
- Parameters:
name (str) – Name of the soft binding to remove
- malloc_key(timeout=120, name=None, cache_timeout=3600)[source]
Allocate a resource key from the pool.
The behavior depends on the allocator’s shared mode: - In non-shared mode (default): Locks the allocated key for exclusive access - In shared mode: Simply removes the key from the free list without locking it
- Parameters:
timeout (float | timedelta | None) – How long the allocation lock should be valid (in seconds).
name (str | None) – Optional name to use for soft binding.
cache_timeout (float | timedelta | None) – Timeout for the soft binding cache entry (seconds). Defaults to 3600. If <= 0, cache entry persists indefinitely.
- Returns:
Resource identifier if allocation was successful, None otherwise
- Return type:
str | None
- malloc(timeout=120, obj=None, params=None, cache_timeout=3600)[source]
Allocate a resource from the pool and wrap it in a RedisAllocatorObject.
If a policy is configured, it will be used to control the allocation behavior. Otherwise, the basic allocation mechanism will be used.
- Parameters:
timeout (float | timedelta | None) – How long the allocation lock should be valid (in seconds)
obj (U | None) – The object to wrap in the RedisAllocatorObject. If it has a .name, soft binding will be attempted.
params (dict | None) – Additional parameters to associate with the allocated object.
cache_timeout (float | timedelta | None) – Timeout for the soft binding cache entry (seconds). Defaults to 3600. Passed to the policy or malloc_key.
- Returns:
RedisAllocatorObject wrapping the allocated resource if successful, None otherwise
- Return type:
RedisAllocatorObject[U] | None
- free(obj, timeout=-1)[source]
Free an allocated object.
- Parameters:
obj (RedisAllocatorObject[U]) – The allocated object to free
timeout (int) – Optional timeout in seconds for the pool item (-1 means no timeout)
- __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.
- delge(key, value)
Deletes a key when the comparison value is greater than or equal to the current value.
- delgt(key, value)
Deletes a key when the comparison value is greater than the current value.
- delle(key, value)
Deletes a key when the comparison value is less than or equal to the current value.
- dellt(key, value)
Deletes a key when the comparison value is less than the current value.
- delne(key, value)
Deletes a key when the comparison value is not equal to the current value.
- health_check()
Check the health status of the keys in the pool.
- is_locked(key)
Check if a key is locked.
- items_locked_status()
Get (key, lock_status) pairs for all keys in the pool.
- key_status(key, timeout=120)
Get the status of a key.
- Parameters:
- Returns:
The current status of the key.
- Return type:
- lock(key, value='1', timeout=120)
Try to lock a key for a specified duration.
- lock_value(key)
Get the value of a locked key.
- 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.
- seteq(key, value, set_value=None, ex=None)
Sets a new value when the comparison value is equal to the current value.
- 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.
- setgt(key, value, set_value=None, ex=None)
Sets a new value when the comparison value is greater than the current value.
- 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.
- setlt(key, value, set_value=None, ex=None)
Sets a new value when the comparison value is less than the current value.
- setne(key, value, set_value=None, ex=None)
Sets a new value when the comparison value is not equal to the current value.
- unlock(key)
Forcefully release a key without checking if the key is locked.
- update(key, value='1', timeout=120)
Lock a key for a specified duration without checking if the key is already locked.
- redis: Redis
RedisAllocatorObject
- class redis_allocator.allocator.RedisAllocatorObject[source]
Represents an object allocated through RedisAllocator.
This class provides an interface for working with allocated objects including locking and unlocking mechanisms for thread-safe operations.
- __init__(allocator, key, obj=None, params=None)[source]
Initialize a RedisAllocatorObject instance.
- Parameters:
allocator (RedisAllocator) – The RedisAllocator that created this object
key (str) – The Redis key for this allocated object
obj (U | None) – The actual object being allocated
params (dict | None) – Additional parameters passed by local program
- allocator: RedisAllocator
- release(timeout=-1)[source]
Close the wrapped object and return the allocation to the pool.
This method is idempotent so callers can safely place it in cleanup paths that may run more than once.
- Parameters:
timeout (int) – Optional timeout in seconds for the returned pool item.
- __exit__(exc_type, exc_value, traceback)[source]
Release this allocation when leaving a
withstatement.
RedisAllocatableClass
- class redis_allocator.allocator.RedisAllocatableClass[source]
A class that can be allocated through RedisAllocator.
You should inherit from this class and implement the set_config method.
RedisThreadHealthCheckPool
- class redis_allocator.allocator.RedisThreadHealthCheckPool[source]
A class that provides a simple interface for managing the health status of a thread.
This class enables tracking the health status of threads in a distributed environment using Redis locks.
Allocator
Redis-based distributed memory allocation system.
This module provides the core functionality of the RedisAllocator system, allowing for distributed memory allocation with support for garbage collection, thread health checking, and priority-based allocation mechanisms.
Key features: 1. Shared vs non-shared allocation modes:
In shared mode, allocating an item simply removes it from the free list and puts it back to the tail
In non-shared mode, allocation locks the item to prevent others from accessing it
Garbage collection for stale/unhealthy items: - Items that are locked (unhealthy) but in the free list are removed - Items that are not in the free list but haven’t been updated within their timeout are freed
Soft binding mechanism: - Maps object names to allocated keys for consistent allocation - Prioritizes previously allocated keys when the same named object requests allocation
Support for an updater to refresh the pool’s keys periodically
Policy-based control of allocation behavior through RedisAllocatorPolicy
- redis_allocator.allocator.ALLOCATED_SENTINEL = '#ALLOCATED'
Sentinel written into the prev/next fields of a pool hash entry to mark the key as currently allocated. Pool keys equal to this string are rejected by
RedisAllocator._validate_keysto avoid collision.
- redis_allocator.allocator.POOL_VALUE_SEP = '||'
Separator between prev / next / expiry inside a pool hash value (“prev||next||expiry”). Pool keys containing this substring are rejected at the API boundary to avoid the encoding being mis-parsed.
- class redis_allocator.allocator.RedisThreadHealthCheckPool[source]
Bases:
RedisLockPoolA class that provides a simple interface for managing the health status of a thread.
This class enables tracking the health status of threads in a distributed environment using Redis locks.
- class redis_allocator.allocator.RedisAllocatableClass[source]
Bases:
ABCA class that can be allocated through RedisAllocator.
You should inherit from this class and implement the set_config method.
- class redis_allocator.allocator.RedisAllocatorObject[source]
Bases:
Generic[U]Represents an object allocated through RedisAllocator.
This class provides an interface for working with allocated objects including locking and unlocking mechanisms for thread-safe operations.
- __init__(allocator, key, obj=None, params=None)[source]
Initialize a RedisAllocatorObject instance.
- Parameters:
allocator (RedisAllocator) – The RedisAllocator that created this object
key (str) – The Redis key for this allocated object
obj (U | None) – The actual object being allocated
params (dict | None) – Additional parameters passed by local program
- allocator: RedisAllocator
- release(timeout=-1)[source]
Close the wrapped object and return the allocation to the pool.
This method is idempotent so callers can safely place it in cleanup paths that may run more than once.
- Parameters:
timeout (int) – Optional timeout in seconds for the returned pool item.
- __exit__(exc_type, exc_value, traceback)[source]
Release this allocation when leaving a
withstatement.
- class redis_allocator.allocator.RedisAllocatorUpdater[source]
Bases:
objectA class that updates the allocator keys.
- class redis_allocator.allocator.RedisAllocatorPolicy[source]
-
Abstract base class for Redis allocator policies.
This class defines the interface for allocation policies that can be used with RedisAllocator to control allocation behavior.
- initialize(allocator)[source]
Initialize the policy with an allocator instance.
- Parameters:
allocator (RedisAllocator[U]) – The RedisAllocator instance to use with this policy
- abstract malloc(allocator, timeout=120, obj=None, params=None, cache_timeout=3600)[source]
Allocate a resource according to the policy.
- Parameters:
allocator (RedisAllocator[U]) – The RedisAllocator instance
timeout (float | timedelta | None) – How long the allocation should be valid (in seconds)
obj (U | None) – The object to associate with the allocation
params (dict | None) – Additional parameters for the allocation
cache_timeout (float | timedelta | None) – Timeout for the soft binding cache entry (seconds). Defaults to 3600.
- Returns:
RedisAllocatorObject if allocation was successful, None otherwise
- Return type:
RedisAllocatorObject[U] | None
- abstract refresh_pool(allocator)[source]
Refresh the allocation pool.
This method is called periodically to update the pool with new resources.
- Parameters:
allocator (RedisAllocator[U]) – The RedisAllocator instance
- abstract refresh_pool_all(allocator)[source]
Refresh the allocation pool.
- Parameters:
allocator (RedisAllocator[U])
- check_health_once(r_obj, duration=3600)[source]
Check the health of the object.
- Parameters:
r_obj (RedisAllocatorObject[U])
duration (int)
- Return type:
- class redis_allocator.allocator.DefaultRedisAllocatorPolicy[source]
Bases:
RedisAllocatorPolicy[U]Default implementation of RedisAllocatorPolicy.
This policy provides the following features: 1. Garbage collection before allocation: Automatically performs garbage collection
operations before allocating resources to ensure stale resources are reclaimed.
Soft binding prioritization: Prioritizes allocation of previously bound keys for named objects, creating a consistent mapping between object names and keys. If a soft binding exists but the bound key is no longer in the pool, the binding is ignored and a new key is allocated.
Periodic pool updates: Uses an optional updater to refresh the pool’s keys at configurable intervals. Only one process/thread (the one that acquires the update lock) will perform the update.
Configurable expiry times: Allows setting default expiry durations for pool items, ensuring automatic cleanup of stale resources even without explicit garbage collection.
The policy controls when garbage collection happens, when the pool is refreshed with new keys, and how allocation prioritizes resources.
- __init__(gc_count=5, update_interval=300, expiry_duration=-1, updater=None, auto_close=False)[source]
Initialize the default allocation policy.
- Parameters:
gc_count (int) – Number of GC operations to perform before allocation
update_interval (int) – Interval in seconds between pool updates
expiry_duration (int) – Default timeout for pool items (-1 means no timeout)
updater (RedisAllocatorUpdater | None) – Optional updater for refreshing the pool’s keys
auto_close (bool) – If True, the allocator will automatically close the object when it is not unique
- initialize(allocator)[source]
Initialize the policy with an allocator instance.
- Parameters:
allocator (RedisAllocator[U]) – The RedisAllocator instance to use with this policy
- refresh_pool_all(allocator)[source]
Refresh the allocation pool.
- Parameters:
allocator (RedisAllocator[U])
- malloc(allocator, timeout=120, obj=None, params=None, cache_timeout=3600)[source]
Allocate a resource according to the policy.
This implementation: 1. Performs GC operations before allocation 2. Checks for soft binding based on object name 3. Falls back to regular allocation if no soft binding exists
- Parameters:
allocator (RedisAllocator[U]) – The RedisAllocator instance
timeout (float | timedelta | None) – How long the allocation should be valid (in seconds)
obj (U | None) – The object to associate with the allocation
params (dict | None) – Additional parameters for the allocation
cache_timeout (float | timedelta | None) – Timeout for the soft binding cache entry (seconds). Defaults to 3600.
- Returns:
RedisAllocatorObject if allocation was successful, None otherwise
- Return type:
RedisAllocatorObject[U] | None
- refresh_pool(allocator, shuffle=True, n=1)[source]
Refresh the allocation pool using the updater.
- Parameters:
allocator (RedisAllocator[U]) – The RedisAllocator instance
- finalize(allocator)[source]
Finalize the policy.
- Parameters:
allocator (RedisAllocator[U])
- class redis_allocator.allocator.RedisAllocator[source]
Bases:
RedisLockPool,Generic[U]A Redis-based distributed allocation system.
Manages a pool of resource identifiers (keys) using Redis, allowing distributed clients to allocate, free, and manage these resources. It leverages Redis’s atomic operations via Lua scripts for safe concurrent access.
The allocator maintains a doubly-linked list in a Redis hash to track available (free) resources. Allocated resources are tracked using standard Redis keys that act as locks.
Key Concepts: - Allocation Pool: A set of resource identifiers (keys) managed by the allocator.
Stored in a Redis hash (<prefix>|<suffix>|pool) representing a doubly-linked list. Head/Tail pointers are stored in separate keys (<prefix>|<suffix>|pool|head, <prefix>|<suffix>|pool|tail).
Free List: The subset of keys within the pool that are currently available. Represented by the linked list structure within the pool hash.
Allocated State: A key is considered allocated if a corresponding lock key exists (<prefix>|<suffix>:<key>).
Shared Mode: If shared=True, allocating a key moves it to the tail of the free list but does not create a lock key. This allows multiple clients to “allocate” the same key concurrently, effectively using the list as a rotating pool. If shared=False (default), allocation creates a lock key, granting exclusive access.
Soft Binding: Allows associating a logical name with an allocated key. If an object provides a name, the allocator tries to reuse the previously bound key for that name. Stored in Redis keys like <prefix>|<suffix>-cache:bind:<name>.
Garbage Collection (GC): Periodically scans the pool to reconcile the free list with the lock states. Removes expired/locked items from the free list and returns items whose locks have expired back to the free list.
Policies: Uses RedisAllocatorPolicy (e.g., DefaultRedisAllocatorPolicy) to customize allocation behavior, GC triggering, and pool updates.
Generic type U should implement RedisAllocatableClass.
- __init__(redis, prefix, suffix='allocator', eps=1e-06, shared=False, policy=None)[source]
Initializes the RedisAllocator.
- Parameters:
redis (Redis) – StrictRedis client instance (must decode responses).
prefix (str) – Prefix for all Redis keys used by this allocator instance.
suffix – Suffix to uniquely identify this allocator instance’s keys.
eps – Small float tolerance for comparisons (used by underlying lock).
shared – If True, operates in shared mode (keys are rotated, not locked). If False (default), keys are locked upon allocation.
policy (RedisAllocatorPolicy[U] | None) – Optional allocation policy. Defaults to DefaultRedisAllocatorPolicy.
- extend(keys=None, timeout=-1)[source]
Add new resources to the allocation pool.
- Parameters:
- Raises:
ValueError – If any key fails
_validate_keys().
- assign(keys=None, timeout=-1)[source]
Completely replace the resources in the allocation pool.
- Parameters:
keys (Sequence[str] | None) – Sequence of resource identifiers to assign to the pool, replacing any existing resources. Each key is validated by
_validate_keys()before being inserted; invalid keys cause the entire call to be rejected (the existing pool is left untouched).timeout (int) – Optional timeout in seconds for the pool items (-1 means no timeout)
- Raises:
ValueError – If any key fails
_validate_keys().
- __contains__(key)[source]
Check if a resource identifier is in the allocation pool.
- Parameters:
key – Resource identifier to check
- Returns:
True if the resource is in the pool, False otherwise
- update_soft_bind(name, key, timeout=3600)[source]
Update a soft binding between a name and a resource.
Soft bindings create a persistent mapping between named objects and allocated keys, allowing the same key to be consistently allocated to the same named object. This is useful for maintaining affinity between objects and their resources.
- unbind_soft_bind(name)[source]
Remove a soft binding.
This removes the persistent mapping between a named object and its allocated key, allowing the key to be freely allocated to any requestor.
- Parameters:
name (str) – Name of the soft binding to remove
- malloc_key(timeout=120, name=None, cache_timeout=3600)[source]
Allocate a resource key from the pool.
The behavior depends on the allocator’s shared mode: - In non-shared mode (default): Locks the allocated key for exclusive access - In shared mode: Simply removes the key from the free list without locking it
- Parameters:
timeout (float | timedelta | None) – How long the allocation lock should be valid (in seconds).
name (str | None) – Optional name to use for soft binding.
cache_timeout (float | timedelta | None) – Timeout for the soft binding cache entry (seconds). Defaults to 3600. If <= 0, cache entry persists indefinitely.
- Returns:
Resource identifier if allocation was successful, None otherwise
- Return type:
str | None
- malloc(timeout=120, obj=None, params=None, cache_timeout=3600)[source]
Allocate a resource from the pool and wrap it in a RedisAllocatorObject.
If a policy is configured, it will be used to control the allocation behavior. Otherwise, the basic allocation mechanism will be used.
- Parameters:
timeout (float | timedelta | None) – How long the allocation lock should be valid (in seconds)
obj (U | None) – The object to wrap in the RedisAllocatorObject. If it has a .name, soft binding will be attempted.
params (dict | None) – Additional parameters to associate with the allocated object.
cache_timeout (float | timedelta | None) – Timeout for the soft binding cache entry (seconds). Defaults to 3600. Passed to the policy or malloc_key.
- Returns:
RedisAllocatorObject wrapping the allocated resource if successful, None otherwise
- Return type:
RedisAllocatorObject[U] | None
- free(obj, timeout=-1)[source]
Free an allocated object.
- Parameters:
obj (RedisAllocatorObject[U]) – The allocated object to free
timeout (int) – Optional timeout in seconds for the pool item (-1 means no timeout)
- redis: Redis
Core Concepts & Visualization
Allocator Pool Structure (Conceptual)
The allocator manages a pool of resources using several Redis keys:
Pool Hash (`<prefix>|<suffix>|pool`): Stores the resource keys currently in the pool. The value for each key represents its position in a doubly-linked list used for the free list (e.g., prev_key||next_key||expiry_timestamp). A special value (#ALLOCATED) indicates the key is part of the pool but not currently free.
Head Pointer (`…|pool|head`): A simple key holding the resource key currently at the head of the free list.
Tail Pointer (`…|pool|tail`): A simple key holding the resource key currently at the tail of the free list.
Lock Keys (`<prefix>|<suffix>:<key>`): Standard Redis keys used as locks in non-shared mode. Their existence indicates a resource is allocated.
Soft Bind Cache Keys (`<prefix>|<suffix>-cache:bind:<name>`): Store the mapping between a logical name and an allocated resource key for soft binding.
graph TD
subgraph Redis Keys
HKey["<prefix>|<suffix>|pool|head"] --> Key1["Key1: ""||Key2||Expiry"]
TKey["<prefix>|<suffix>|pool|tail"] --> KeyN["KeyN: KeyN-1||""||Expiry"]
PoolHash["<prefix>|<suffix>|pool (Hash)"]
end
subgraph "PoolHash Contents (Doubly-Linked Free List)"
Key1 --> Key2["Key2: Key1||Key3||Expiry"]
Key2 --> Key3["Key3: Key2||...||Expiry"]
Key3 --> ...
KeyN_1[...] --> KeyN
end
subgraph "Allocated Keys (Non-Shared Mode)"
LKey1["<prefix>|<suffix>:AllocatedKey1"]
LKeyX["<prefix>|<suffix>:AllocatedKeyX"]
end
subgraph "Soft Bind Cache"
CacheKey1["<prefix>|<suffix>-cache:bind:name1"] --> LKey1
end
Atomicity Guarantees
Allocator state transitions are implemented as Redis Lua scripts registered through
register_script. Redis runs each script atomically on a single Redis instance, so
multiple client processes cannot observe or interleave a half-updated free-list node,
lock key, or soft-binding update. Redis Cluster is intentionally unsupported because
the allocator mutates several related keys in the same script.
The atomic boundary is one public call for the core pool operations:
Operation |
Atomic boundary |
Notes |
|---|---|---|
|
One Lua EVAL |
Checks soft binding, pops or rotates the free-list head, marks the item allocated, creates the lock key, and refreshes binding state together. |
|
One Lua EVAL |
Deletes lock keys and returns still-present pool members to the free-list tail together. Unknown or evicted keys are no-ops. |
|
One Lua EVAL per call |
Batch updates are atomic as a batch. |
|
One Lua EVAL |
Deletes the pool hash plus head/tail pointers only; it does not scan or delete per-resource lock keys. |
|
One Lua EVAL per pass |
Incrementally reconciles up to the requested scan count and saves the scan cursor. |
|
Wrapper around atomic calls |
|
Multi-process recovery relies on atomic scripts plus lock TTLs and garbage collection.
A process killed after allocation may leave a lock behind; pool-member locks either
expire naturally or are reconciled by gc(). Orphan locks whose key is no longer
in the pool are outside GC scope. Permanent locks created with timeout <= 0 are
never reclaimed automatically and must be released explicitly with free_keys(key),
by reintroducing that same key with extend([key]) or assign([... key ...]),
or by deleting <prefix>|<suffix>:<key> directly.
Complexity
The allocator hot path avoids full-pool scans. malloc_key() and single-key
free_keys() are the intended high-frequency operations; assign() is for
lower-frequency wholesale replacement.
Operation |
Time complexity |
Space / Redis writes |
Recommended use |
|---|---|---|---|
|
Amortized |
Constant extra metadata writes |
Hot path allocation. |
|
|
Constant writes per released key |
Hot path release; single-key free is |
|
|
Constant writes per supplied key |
Incremental pool additions or expiry refresh. |
|
|
Constant writes per supplied key |
Remove known resources. |
|
|
Rewrites membership for removed or new keys |
Low-frequency full replacement. |
|
Approximately |
Bounded by scanned entries |
Periodic incremental repair and reclaim. |
k in malloc_key() is the number of stale head entries cleaned during that
allocation. Those entries are removed or marked allocated as they are skipped, so
the cost is paid once and amortizes over later calls.
The pytest -m benchmark suite enforces these contracts across pool sizes from
10 to 5000 keys for malloc_key() plus single-key free_keys(), batched
free_keys(n), extend(n), shrink(n), and gc(count). It covers
assign(n) across 10 to 1000 keys because fakeredis/lupa is unreliable with
5000+ Lua ARGV on Windows. tests/_perf_real_redis.py runs the same benchmark
against a real Redis container and covers assign(n) through 5000 keys.
Allocation Wrapper Lifecycle
RedisAllocator.malloc() returns None when no resource is available. When it returns a RedisAllocatorObject, prefer using it as a context manager:
allocation = allocator.malloc(timeout=120)
if allocation is not None:
with allocation:
use_resource(allocation.key)
Leaving the with block calls allocation.release(), which closes the wrapped object and returns the key to the allocator. release() is idempotent and can also be called directly in cleanup paths. close() only closes the wrapped object; it does not return the key to the pool.