### Install python-redis-lock Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Install the core library or with optional extras for Redis client, Django integration, or Valkey support. ```bash pip install python-redis-lock ``` ```bash pip install "python-redis-lock[redis]" ``` ```bash pip install "python-redis-lock[django]" ``` ```bash pip install "python-redis-lock[valkey]" ``` -------------------------------- ### Install python-redis-lock with Django support Source: https://github.com/ionelmc/python-redis-lock/blob/master/README.rst Command to install the library with the necessary dependencies for Django integration. This enables the use of Redis as a Django cache backend. ```bash pip install "python-redis-lock[django]" ``` -------------------------------- ### Import redis-lock Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/usage.md Import the necessary library to start using redis-lock. ```python import redis_lock ``` -------------------------------- ### Acquire and Release a Lock Source: https://github.com/ionelmc/python-redis-lock/blob/master/README.rst Demonstrates how to acquire a lock non-blockingly and release it. Ensure you have a Redis connection and the redis_lock library installed. ```python from redis import Redis conn = Redis() import redis_lock lock = redis_lock.Lock(conn, "name-of-the-lock") if lock.acquire(blocking=False): print("Got the lock.") lock.release() else: print("Someone else has the lock.") ``` -------------------------------- ### Get or Create Lock with Cache Source: https://github.com/ionelmc/python-redis-lock/blob/master/README.rst Use this pattern to get a value from cache, and if it's not present, acquire a lock to compute and set the value. Ensure the lock is released after use. ```python from django.core.cache import cache def function(): val = cache.get(key) if not val: with cache.lock(key): val = cache.get(key) if not val: # DO EXPENSIVE WORK val = ... cache.set(key, value) return val ``` -------------------------------- ### Configure Django Cache Backend Source: https://github.com/ionelmc/python-redis-lock/blob/master/README.rst Example Django settings to configure the RedisCache backend provided by python-redis-lock. This backend adds a convenient .lock() method for cache operations. ```python CACHES = { 'default': { 'BACKEND': 'redis_lock.django_cache.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient' } } } ``` -------------------------------- ### Reset All Locks Source: https://github.com/ionelmc/python-redis-lock/blob/master/README.rst Use the `reset_all()` function on application start or restart to clear all existing locks in Redis. This is a troubleshooting step for persistent locks. ```python # On application start/restart import redis_lock redis_lock.reset_all() ``` -------------------------------- ### Logger Configuration: Suppress All Debug Output Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Configure the standard Python `logging` module to control `redis_lock` output. This example suppresses all debug output from the `redis_lock` logger by setting its level to `WARNING`. ```python import logging # Suppress all redis_lock debug output logging.getLogger("redis_lock").setLevel(logging.WARNING) ``` -------------------------------- ### Prevent Dogpile with Locked Get or Set Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Utilize `cache.locked_get_or_set` to prevent multiple requests from computing the same expensive data simultaneously. This function acquires a lock, computes the value if necessary, and then stores it in the cache. ```python from django.core.cache import cache import time def get_expensive_report(report_id): def compute(): time.sleep(2) # Simulate expensive computation return {"data": "expensive result", "id": report_id} return cache.locked_get_or_set( key=f"report:{report_id}", value_creator=compute, expire=10, # lock expiry in seconds timeout=300, # cache TTL in seconds ) ``` -------------------------------- ### Retrieve the current Redis lock owner ID Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Use `lock.get_owner_id()` to get the unique identifier of the current lock holder. Returns `None` if the lock is not held. This is useful for verifying ownership or debugging. ```python from redis import StrictRedis import socket import redis_lock conn = StrictRedis() host_id = f"host:{socket.gethostname()}" lock = redis_lock.Lock(conn, "exclusive-job", id=host_id) lock.acquire() # From another part of the application: checker = redis_lock.Lock(conn, "exclusive-job") owner = checker.get_owner_id() if owner == host_id: print("This machine already holds the lock.") elif owner is not None: print(f"Lock is held by: {owner}") else: print("Lock is free.") lock.release() ``` -------------------------------- ### Run All Checks with Tox Source: https://github.com/ionelmc/python-redis-lock/blob/master/CONTRIBUTING.rst Execute all project checks and the documentation builder using tox. ```bash tox ``` -------------------------------- ### Clone Repository Source: https://github.com/ionelmc/python-redis-lock/blob/master/CONTRIBUTING.rst Clone your forked repository locally to begin development. ```bash git clone git@github.com:YOURGITHUBNAME/python-redis-lock.git ``` -------------------------------- ### Run All Test Environments in Parallel Source: https://github.com/ionelmc/python-redis-lock/blob/master/CONTRIBUTING.rst Utilize tox to run all configured test environments concurrently for faster feedback. ```bash tox -p auto ``` -------------------------------- ### Run Benchmarks Source: https://github.com/ionelmc/python-redis-lock/blob/master/examples/bench.rst Execute the benchmark script using tox. Ensure a Redis server is running on the default port. The script will run for 10 seconds, testing lock acquisition and release under different concurrency settings. ```bash tox -e py38-dj3-cover -- python examples/bench.py 10 ``` -------------------------------- ### Create Feature Branch Source: https://github.com/ionelmc/python-redis-lock/blob/master/CONTRIBUTING.rst Create a new branch for your local development work. ```bash git checkout -b name-of-your-bugfix-or-feature ``` -------------------------------- ### Valkey Cache Backend Configuration Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Configure the `ValkeyCache` backend in `settings.py` to use Valkey-compatible stores. This backend offers an identical API to the Django Redis backend. ```python # settings.py CACHES = { 'default': { 'BACKEND': 'redis_lock.django_valkey_cache.ValkeyCache', 'LOCATION': 'valkey://127.0.0.1:6379/1', 'OPTIONS': { 'CLIENT_CLASS': 'django_valkey.client.DefaultClient', }, } } ``` -------------------------------- ### Use Lock as a Context Manager Source: https://github.com/ionelmc/python-redis-lock/blob/master/README.rst Shows how to use the Redis Lock as a context manager for automatic acquisition and release. This is the recommended way to use locks. ```python from redis import StrictRedis conn = StrictRedis() with redis_lock.Lock(conn, "name-of-the-lock"): print("Got the lock. Doing some work ...") time.sleep(5) ``` -------------------------------- ### Commit and Push Changes Source: https://github.com/ionelmc/python-redis-lock/blob/master/CONTRIBUTING.rst Stage all changes, commit them with a descriptive message, and push the branch to GitHub. ```bash git add . git commit -m "Your detailed description of your changes." git push origin name-of-your-bugfix-or-feature ``` -------------------------------- ### Lock.__init__ Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Instantiates a Lock object bound to a Redis key. The lock is not acquired until acquire() is called or the context manager is entered. ```APIDOC ## Lock.__init__ - Create a named distributed lock ### Description Instantiates a `Lock` object bound to a Redis key. The lock is not acquired until `acquire()` is called (or the context manager is entered). Raises `ValueError` if `auto_renewal=True` is passed without an `expire` value. ### Method Signature `redis_lock.Lock(conn, name, expire=None, auto_renewal=False, blocking=True, id=None, timeout=None)` ### Parameters #### Path Parameters - **conn** (redis.Redis) - Required - Redis connection object. - **name** (str) - Required - The name of the lock. #### Query Parameters - **expire** (int, optional) - The expiration time of the lock in seconds. - **auto_renewal** (bool, optional) - If True, the lock will be automatically renewed in the background. Requires `expire` to be set. - **blocking** (bool, optional) - If True, `acquire()` will block until the lock is obtained. Defaults to True. - **id** (str, optional) - A custom identifier for the lock owner. - **timeout** (int, optional) - The maximum time in seconds to wait for the lock to be acquired when `blocking` is True. ### Request Example ```python from redis import StrictRedis import redis_lock conn = StrictRedis(host='localhost', port=6379, db=0) # Basic lock — no expiry, blocking by default lock = redis_lock.Lock(conn, "my-resource") # Lock with 30-second expiry lock = redis_lock.Lock(conn, "my-resource", expire=30) # Lock with auto-renewal: expires after 60s but renews every 40s automatically lock = redis_lock.Lock(conn, "my-resource", expire=60, auto_renewal=True) # Lock with a custom ID (useful for identifying the owner) import socket host_id = f"worker-{socket.gethostname()}" lock = redis_lock.Lock(conn, "my-resource", id=host_id) # Non-blocking by default when used as context manager lock = redis_lock.Lock(conn, "my-resource", expire=10, blocking=False) ``` ``` -------------------------------- ### Valkey Cache Backend Usage Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Usage of the Valkey cache backend is identical to the standard Redis backend. You can use `cache.lock` and `cache.locked_get_or_set` as usual. ```python from django.core.cache import cache with cache.lock("valkey-backed-lock", expire=60): print("Holding lock on Valkey.") result = cache.locked_get_or_set( key="cached-data", value_creator=lambda: {"computed": True}, timeout=120, ) ``` -------------------------------- ### Run Subset of Tests with Tox Source: https://github.com/ionelmc/python-redis-lock/blob/master/CONTRIBUTING.rst Execute a specific subset of tests by specifying the environment name and test keyword. ```bash tox -e envname -- pytest -k test_myfeature ``` -------------------------------- ### reset_all Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Scans for all keys matching 'lock:*' and deletes them, signaling all waiting processes. Intended for application startup after a crash to clear any lingering locks. ```APIDOC ## reset_all — Forcibly delete all locks in Redis Scans for all keys matching `lock:*` and deletes them, signaling all waiting processes. Intended for application startup after a crash to clear any lingering locks. ### Method `reset_all(conn: StrictRedis) -> int` ### Parameters - **conn** (StrictRedis) - Required - The Redis connection object. ### Request Example ```python from redis import StrictRedis import redis_lock conn = StrictRedis() # Call at application start/restart to clear any leftover locks count = redis_lock.reset_all(conn) print(f"Cleared all distributed locks.") ``` ### Response - **count** (int) - The number of locks cleared. ``` -------------------------------- ### Use Lock as a Context Manager Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/usage.md Utilize the lock as a context manager for automatic acquisition and release. This is the recommended approach for ensuring locks are always released. ```python conn = StrictRedis() with redis_lock.Lock(conn, "name-of-the-lock"): print("Got the lock. Doing some work ...") time.sleep(5) ``` -------------------------------- ### Lock as a context manager Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Using Lock as a context manager calls acquire() on entry and release() on exit. If the lock is not acquired and blocking=True (default), a NotAcquired exception is raised on entry. ```python from redis import StrictRedis import redis_lock import time conn = StrictRedis() # Standard blocking context manager usage with redis_lock.Lock(conn, "critical-section", expire=30): print("Inside the critical section.") time.sleep(2) # Lock is automatically released here # Non-blocking context manager — raises NotAcquired if lock is unavailable from redis_lock import NotAcquired try: with redis_lock.Lock(conn, "critical-section", blocking=False): print("Got the lock, doing fast work.") except NotAcquired: print("Lock unavailable, skipping this run.") ``` -------------------------------- ### Lock as a context manager Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Using Lock as a context manager automatically calls acquire() on entry and release() on exit. ```APIDOC ## Lock as a context manager - Automatic acquire and release ### Description Using `Lock` as a context manager calls `acquire()` on entry and `release()` on exit (even if an exception is raised). If the lock is not acquired and `blocking=True` (default), a `NotAcquired` exception is raised on entry. ### Method Signature `with redis_lock.Lock(...) as lock:` ### Request Example ```python from redis import StrictRedis import redis_lock import time conn = StrictRedis() # Standard blocking context manager usage with redis_lock.Lock(conn, "critical-section", expire=30): print("Inside the critical section.") time.sleep(2) # Lock is automatically released here # Non-blocking context manager — raises NotAcquired if lock is unavailable from redis_lock import NotAcquired try: with redis_lock.Lock(conn, "critical-section", blocking=False): print("Got the lock, doing fast work.") except NotAcquired: print("Lock unavailable, skipping this run.") ``` ### Response #### Success Response - The code block within the `with` statement is executed. #### Error Response - **NotAcquired**: Raised on context entry if the lock could not be acquired and `blocking=False`. ``` -------------------------------- ### Exceptions Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/reference/index.md Custom exceptions raised by the redis-lock library. ```APIDOC ## Exceptions ### Description Custom exceptions for handling specific lock-related errors. ### Exception Types - `AlreadyAcquired`: Raised when attempting to acquire a lock that is already held. - `AlreadyStarted`: Raised when an operation is attempted on an already started process. - `InvalidTimeout`: Raised when an invalid timeout value is provided. - `NotAcquired`: Raised when a lock cannot be acquired. - `NotExpirable`: Raised when a lock cannot be set to expire. - `TimeoutNotUsable`: Raised when a timeout value is not usable. - `TimeoutTooLarge`: Raised when a timeout value exceeds the maximum allowed. ``` -------------------------------- ### Django Cache Backend with Lock Support Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Configure Django settings to use `redis_lock.django_cache.RedisCache` as a drop-in replacement for `django-redis`. This backend adds `.lock()` and `.locked_get_or_set()` methods for integrated caching and locking. ```python # settings.py CACHES = { 'default': { 'BACKEND': 'redis_lock.django_cache.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', }, } } ``` -------------------------------- ### Lock with Owner Identifier Source: https://github.com/ionelmc/python-redis-lock/blob/master/README.rst Illustrates how to associate an identifier with a lock to track ownership across processes or machines. This is useful for identifying who currently holds the lock. ```python import socket host_id = "owned-by-%s" % socket.gethostname() lock = redis_lock.Lock(conn, "name-of-the-lock", id=host_id) if lock.acquire(blocking=False): assert lock.locked() is True print("Got the lock.") lock.release() else: if lock.get_owner_id() == host_id: print("I already acquired this in another process.") else: print("The lock is held on another machine.") ``` -------------------------------- ### Utility Functions Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/reference/index.md Provides utility functions for managing locks globally. ```APIDOC ## Utility Functions ### Description Functions for global lock management. ### Functions - `reset_all(client)`: Resets all locks managed by the given Redis client. ``` -------------------------------- ### Acquire a Blocking Lock Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/usage.md Acquire a lock that will block until it is available. Ensure you have a Redis connection established. ```python conn = StrictRedis() lock = redis_lock.Lock(conn, "name-of-the-lock") if lock.acquire(): print("Got the lock. Doing some work ...") time.sleep(5) ``` -------------------------------- ### Lock.acquire Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Attempts to acquire the lock. Returns True on success and False if the lock could not be obtained. ```APIDOC ## Lock.acquire - Acquire the lock ### Description Attempts to acquire the lock. Returns `True` on success and `False` if the lock could not be obtained (only when `blocking=False` or `timeout` is reached). Raises `AlreadyAcquired` if called again on an already-held lock instance. Raises `TimeoutNotUsable` if both `blocking=False` and `timeout` are specified. Raises `InvalidTimeout` for negative timeout values. Raises `TimeoutTooLarge` if `timeout > expire` (without auto-renewal). ### Method Signature `acquire(blocking=True, timeout=None)` ### Parameters #### Path Parameters - **blocking** (bool, optional) - If True, wait until the lock is available. Defaults to True. - **timeout** (int, optional) - Maximum time in seconds to wait for the lock. If None, waits indefinitely when `blocking` is True. ### Request Example ```python from redis import StrictRedis import redis_lock conn = StrictRedis() # Blocking acquire — waits indefinitely until the lock is available lock = redis_lock.Lock(conn, "job-queue", expire=60) if lock.acquire(): try: print("Lock acquired, processing job...") finally: lock.release() # Non-blocking acquire — returns immediately lock = redis_lock.Lock(conn, "job-queue") if lock.acquire(blocking=False): try: print("Got the lock.") finally: lock.release() else: print("Lock is busy, skipping.") # Blocking with timeout — waits up to 5 seconds lock = redis_lock.Lock(conn, "job-queue", expire=30) if lock.acquire(timeout=5): try: print("Acquired within timeout window.") finally: lock.release() else: print("Could not acquire lock within 5 seconds.") ``` ### Response #### Success Response (bool) - **True**: Lock acquired successfully. - **False**: Lock could not be acquired within the specified timeout or `blocking=False`. #### Error Response - **AlreadyAcquired**: If called on an already-held lock instance. - **TimeoutNotUsable**: If both `blocking=False` and `timeout` are specified. - **InvalidTimeout**: For negative timeout values. - **TimeoutTooLarge**: If `timeout > expire` (without auto-renewal). ``` -------------------------------- ### Acquire a Blocking Lock with Timeout Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/usage.md Acquire a lock that will block for a specified timeout period. If the lock is not acquired within the timeout, the acquisition fails. ```python conn = StrictRedis() lock = redis_lock.Lock(conn, "name-of-the-lock") if lock.acquire(timeout=3): print("Got the lock. Doing some work ...") time.sleep(5) else: print("Someone else has the lock.") ``` -------------------------------- ### Acquire the lock Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Attempts to acquire the lock. Returns True on success and False if the lock could not be obtained (only when blocking=False or timeout is reached). ```python from redis import StrictRedis import redis_lock conn = StrictRedis() # Blocking acquire — waits indefinitely until the lock is available lock = redis_lock.Lock(conn, "job-queue", expire=60) if lock.acquire(): try: print("Lock acquired, processing job...") finally: lock.release() # Non-blocking acquire — returns immediately lock = redis_lock.Lock(conn, "job-queue") if lock.acquire(blocking=False): try: print("Got the lock.") finally: lock.release() else: print("Lock is busy, skipping.") # Blocking with timeout — waits up to 5 seconds lock = redis_lock.Lock(conn, "job-queue", expire=30) if lock.acquire(timeout=5): try: print("Acquired within timeout window.") finally: lock.release() else: print("Could not acquire lock within 5 seconds.") ``` -------------------------------- ### Django Cache Backend Source: https://context7.com/ionelmc/python-redis-lock/llms.txt A Django cache backend that integrates Redis locks, providing `.lock()` and `.locked_get_or_set()` methods. ```APIDOC ## Django Cache Backend (`redis_lock.django_cache.RedisCache`) — Django integration with lock support A drop-in replacement for `django-redis`'s `RedisCache` backend that adds a `.lock()` method and `.locked_get_or_set()` helper. Configure it in Django settings, then use `cache.lock()` anywhere `django.core.cache.cache` is used. ### Configuration Example ```python # settings.py CACHES = { 'default': { 'BACKEND': 'redis_lock.django_cache.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', }, } } ``` ### Usage Example ```python # views.py / service layer from django.core.cache import cache import time # Example using the .lock() method with cache.lock("my-resource", expire=30): # Critical section code here print("Acquired lock and performing operations...") time.sleep(10) print("Releasing lock.") # Example using .locked_get_or_set() value = cache.locked_get_or_set("my-data-key", lambda: fetch_data_from_source(), timeout=60) print(f"Retrieved value: {value}") ``` ### Methods - **`.lock(key: str, expire: int = None, auto_renewal: bool = False)`**: Returns a `Lock` object for the given key. - **`.locked_get_or_set(key: str, default: Callable, timeout: int = None)`**: Gets a value from the cache, or if it doesn't exist, acquires a lock, computes the value using the `default` callable, sets it in the cache, and releases the lock. Returns the computed value. ``` -------------------------------- ### Logger Configuration: Log All Acquire Attempts Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Set the `redis_lock.acquire` logger level to `DEBUG` to capture all lock acquisition attempts, including detailed debug information. ```python import logging # Log all acquire attempts (DEBUG + INFO + WARNING) logging.getLogger("redis_lock.acquire").setLevel(logging.DEBUG) ``` -------------------------------- ### Handle AlreadyAcquired Exception Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Use a try-except block to catch `redis_lock.AlreadyAcquired` when attempting to acquire a lock that is already held. This prevents errors when multiple processes try to acquire the same lock simultaneously. ```python lock = redis_lock.Lock(conn, "demo", expire=10) try: lock.acquire() lock.acquire() # Raises AlreadyAcquired except redis_lock.AlreadyAcquired: print("Already holding this lock.") ``` -------------------------------- ### Basic Lock with Django Cache Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Use `cache.lock` for basic locking within a Django application. Ensure the lock expires after a specified duration to prevent deadlocks. ```python from django.core.cache import cache import time def process_payment(order_id): with cache.lock(f"payment:{order_id}", expire=30): print(f"Processing payment for order {order_id}") time.sleep(1) ``` -------------------------------- ### Acquire a Non-blocking Lock Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/usage.md Attempt to acquire a lock immediately without blocking. If the lock is already held, the acquisition will fail. ```python conn = StrictRedis() lock = redis_lock.Lock(conn, "name-of-the-lock") if lock.acquire(blocking=False): print("Got the lock. Doing some work ...") time.sleep(5) else: print("Someone else has the lock.") ``` -------------------------------- ### Use Non-blocking Lock as Context Manager Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/usage.md Use a non-blocking lock within a context manager. If the lock cannot be acquired immediately, a NotAcquired exception will be raised. ```python conn = StrictRedis() with redis_lock.Lock(conn, "name-of-the-lock", blocking=False): print("Got the lock. Doing some work ...") time.sleep(5) ``` -------------------------------- ### Global Reset Function Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/reference/redis_lock.md The `reset_all` function forcibly deletes all locks, which can be useful after a crash. ```APIDOC ## redis_lock.reset_all(redis_client) Forcibly deletes all locks if its remains (like a crash reason). Use this with care. * **Parameters:** **redis_client** – An instance of `StrictRedis`. ``` -------------------------------- ### Exception Reference: Import Exceptions Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Import relevant exceptions from `redis_lock` to handle specific error conditions. All exceptions are subclasses of `RuntimeError`, `ValueError`, or `TypeError`. ```python from redis_lock import ( AlreadyAcquired, NotAcquired, AlreadyStarted, TimeoutNotUsable, InvalidTimeout, TimeoutTooLarge, NotExpirable, ) from redis import StrictRedis import redis_lock conn = StrictRedis() ``` -------------------------------- ### Renew Lock Automatically with Timeout Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/readme.md Acquire a lock with a specified lifetime and enable automatic renewal to ensure it remains active as long as the process is running. ```python # Get a lock with a 60-second lifetime but keep renewing it automatically # to ensure the lock is held for as long as the Python process is running. with redis_lock.Lock(conn, name='my-lock', expire=60, auto_renewal=True): # Do work.... pass ``` -------------------------------- ### Acquire Lock with Auto-Renewal Source: https://github.com/ionelmc/python-redis-lock/blob/master/README.rst Acquire a lock with a specified expiry time, and enable auto-renewal to keep the lock active as long as the Python process is running. Useful for long-running operations. ```python # Get a lock with a 60-second lifetime but keep renewing it automatically # to ensure the lock is held for as long as the Python process is running. with redis_lock.Lock(conn, name='my-lock', expire=60, auto_renewal=True): # Do work.... ``` -------------------------------- ### Create a named distributed lock Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Instantiate a Lock object bound to a Redis key. Raises ValueError if auto_renewal=True is passed without an expire value. ```python from redis import StrictRedis import redis_lock conn = StrictRedis(host='localhost', port=6379, db=0) # Basic lock — no expiry, blocking by default lock = redis_lock.Lock(conn, "my-resource") # Lock with 30-second expiry lock = redis_lock.Lock(conn, "my-resource", expire=30) # Lock with auto-renewal: expires after 60s but renews every 40s automatically lock = redis_lock.Lock(conn, "my-resource", expire=60, auto_renewal=True) # Lock with a custom ID (useful for identifying the owner) import socket host_id = f"worker-{socket.gethostname()}" lock = redis_lock.Lock(conn, "my-resource", id=host_id) # Non-blocking by default when used as context manager lock = redis_lock.Lock(conn, "my-resource", expire=10, blocking=False) ``` -------------------------------- ### Handle NotAcquired Exception during Extend or Release Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Use a try-except block to catch `redis_lock.NotAcquired` when extending or releasing a lock. This exception is raised if the lock has expired or was reset externally before the operation could be completed. The `finally` block ensures that a release attempt is made, even if the lock was lost. ```python lock.extend() # Raises if lock expired externally except redis_lock.NotAcquired: print("Lock was lost (expired or reset).") finally: try: lock.release() except redis_lock.NotAcquired: pass ``` -------------------------------- ### Reset All Redis Locks Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Use `cache.reset_all()` to clear all locks managed by the cache backend. This is typically used in management commands for emergency situations. ```python from django.core.cache import cache def emergency_lock_reset(): cache.reset_all() print("All Redis locks cleared.") ``` -------------------------------- ### Lock Class Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/reference/redis_lock.md The Lock class provides a context manager for acquiring and releasing distributed locks. It supports blocking, timeouts, and auto-renewal of locks. ```APIDOC ## class redis_lock.Lock(redis_client, name, expire=None, id=None, auto_renewal=False, signal_expire=1000, blocking=True) A Lock context manager implemented via redis SETNX/BLPOP. ### Parameters * **redis_client** – An instance of `StrictRedis`. * **name** – The name (redis key) the lock should have. * **expire** – The lock expiry time in seconds. If left at the default (None) the lock will not expire. * **id** – The ID (redis value) the lock should have. A random value is generated when left at the default. Note that if you specify this then the lock is marked as “held”. Acquires won’t be possible. * **auto_renewal** – If set to `True`, Lock will automatically renew the lock so that it doesn’t expire for as long as the lock is held (acquire() called or running in a context manager). Implementation note: Renewal will happen using a daemon thread with an interval of `expire*2/3`. If wishing to use a different renewal time, subclass Lock, call `super().__init__()` then set `self._lock_renewal_interval` to your desired interval. * **signal_expire** – Advanced option to override signal list expiration in milliseconds. Increase it for very slow clients. Default: `1000`. * **blocking** – Boolean value specifying whether lock should be blocking or not. Used in `__enter__` method. ## acquire(blocking=True, timeout=None) * **Parameters:** * **blocking** – Boolean value specifying whether lock should be blocking or not. * **timeout** – An integer value specifying the maximum number of seconds to block. ## extend(expire=None) Extends expiration time of the lock. * **Parameters:** **expire** – New expiration time. If `None` - expire provided during lock initialization will be taken. ## get_owner_id() ## locked() Return true if the lock is acquired. Checks that lock with same name already exists. This method returns true, even if lock have another id. ## release() Releases the lock, that was acquired with the same object. NOTE: If you want to release a lock that you acquired in a different place you have two choices: * Use `Lock("name", id=id_from_other_place).release()` * Use `Lock("name").reset()` ## reset() Forcibly deletes the lock. Use this with care. ## classmethod register_scripts(redis_client) ``` -------------------------------- ### Control Logging Output Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/usage.md Modify the logging configuration for redis_lock to control the verbosity or disable specific loggers. ```python logging.getLogger("redis_lock.thread").disabled = True logging.getLogger("redis_lock").disable(logging.DEBUG) ``` -------------------------------- ### Lock.extend Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Resets the TTL on the lock key, extending its expiration time. Raises NotAcquired if the lock is not held, NotExpirable if no expiry is set, or ValueError for negative expire values. ```APIDOC ## Lock.extend — Extend the lock expiration time Resets the TTL on the lock key. Useful for manually extending a long-running operation's lock before it expires. Raises `NotAcquired` if the lock is not currently held. Raises `NotExpirable` if the lock has no expiry set. Raises `ValueError` for negative expire values. ### Method `extend(expire: int)` ### Parameters - **expire** (int) - Required - The new expiration time in seconds. ### Request Example ```python from redis import StrictRedis import redis_lock import time conn = StrictRedis() lock = redis_lock.Lock(conn, "batch-job", expire=10) lock.acquire() try: for i in range(5): print(f"Processing chunk {i}...") time.sleep(3) # Extend by another 10 seconds before expiry lock.extend(expire=10) print("Lock expiry extended.") finally: lock.release() ``` ### Response None (raises exceptions on failure) ``` -------------------------------- ### Lock.reset Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Forcibly deletes a specific lock's Redis key, signaling waiting processes. This operation does not check for lock ownership. ```APIDOC ## Lock.reset — Forcibly delete a specific lock Deletes the lock's Redis key and signals waiting processes unconditionally. Use only for crash recovery or administrative cleanup — it does not check lock ownership. ### Method `reset()` ### Parameters None ### Request Example ```python from redis import StrictRedis import redis_lock conn = StrictRedis() # Force-clear a stuck lock (e.g., after a crash) stale_lock = redis_lock.Lock(conn, "crashed-worker-lock") stale_lock.reset() print("Stale lock cleared.") ``` ### Response None ``` -------------------------------- ### Release Lock with Matching ID Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/usage.md Release a lock only if it has a specific ID, ensuring that you are releasing the exact lock instance that was acquired. ```python lock1 = Lock(conn, "foo") lock1.acquire() lock2 = Lock(conn, "foo", id=lock1.id) lock2.release() ``` -------------------------------- ### Lock Class Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/reference/index.md The Lock class provides the primary interface for acquiring, extending, and releasing distributed locks. ```APIDOC ## Lock Class ### Description Represents a distributed lock that can be acquired and released. ### Methods - `__init__(self, client, name, timeout=None, blocking=True, blocking_timeout=None, lock_timeout=None)`: Initializes a new Lock instance. - `acquire(self)`: Attempts to acquire the lock. - `blocking(self)`: Returns whether the lock acquisition is blocking. - `extend(self, timeout=None)`: Extends the expiration time of an acquired lock. - `extend_script(self)`: Returns the Lua script for extending the lock. - `get_owner_id(self)`: Retrieves the unique identifier of the lock owner. - `id(self)`: Returns the unique identifier of the lock. - `locked(self)`: Checks if the lock is currently held. - `register_scripts(self)`: Registers necessary Lua scripts with the Redis client. - `release(self)`: Releases the acquired lock. - `reset(self)`: Resets the lock state (internal use). - `reset_all_script(self)`: Returns the Lua script for resetting all locks. - `reset_script(self)`: Returns the Lua script for resetting a specific lock. - `unlock_script(self)`: Returns the Lua script for releasing the lock. ``` -------------------------------- ### Forcibly delete all Redis locks Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Call `redis_lock.reset_all(conn)` to scan for and delete all keys matching the `lock:*` pattern. This is intended for use during application startup after a crash to clear any lingering distributed locks. ```python from redis import StrictRedis import redis_lock conn = StrictRedis() # Call at application start/restart to clear any leftover locks count = redis_lock.reset_all(conn) print(f"Cleared all distributed locks.") ``` -------------------------------- ### Handle TimeoutNotUsable Exception Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Catch `redis_lock.TimeoutNotUsable` to handle the error that occurs when `blocking=False` is used with a `timeout` argument during lock acquisition. These two parameters are mutually exclusive. ```python redis_lock.Lock(conn, "demo", blocking=False, expire=10).acquire(timeout=5) except redis_lock.TimeoutNotUsable: print("Cannot combine blocking=False with timeout.") ``` -------------------------------- ### Lock.locked Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Checks if the lock is currently held by any process or instance. Returns True if held, False otherwise. ```APIDOC ## Lock.locked — Check if the lock is currently held Returns `True` if any holder currently owns the lock (regardless of which instance or process holds it). Useful for monitoring or conditional logic. ### Method `locked() -> bool` ### Parameters None ### Request Example ```python from redis import StrictRedis import redis_lock conn = StrictRedis() lock = redis_lock.Lock(conn, "resource-x") print(lock.locked()) # False — no one holds it lock.acquire() print(lock.locked()) # True # Check from a completely separate Lock instance with the same name other = redis_lock.Lock(conn, "resource-x") print(other.locked()) # True — same Redis key is held lock.release() print(lock.locked()) # False ``` ### Response - **locked** (bool) - True if the lock is held, False otherwise. ``` -------------------------------- ### Logger Configuration: Disable Auto-Renewal Thread Logs Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Disable logs specifically from the `redis_lock.refresh.thread` logger. This allows you to control the verbosity of the auto-renewal mechanism. ```python import logging # Disable only the auto-renewal thread logs logging.getLogger("redis_lock.refresh.thread").disabled = True ``` -------------------------------- ### Release a Previously Acquired Lock Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/usage.md Explicitly release a lock that has been acquired. This should be done after the critical section is complete. ```python conn = StrictRedis() lock = redis_lock.Lock(conn, "name-of-the-lock") lock.acquire() print("Got the lock. Doing some work ...") time.sleep(5) lock.release() ``` -------------------------------- ### Release a Redis lock Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Manually release a lock using `lock.release()`. This is useful for explicit cleanup. Ensure to handle `NotAcquired` exceptions, which occur if the lock has already expired or was released externally. The auto-renewal thread is also stopped upon release. ```python from redis import StrictRedis import redis_lock from redis_lock import NotAcquired conn = StrictRedis() lock = redis_lock.Lock(conn, "my-resource", expire=30) lock.acquire() try: print("Doing work...") finally: try: lock.release() print("Lock released.") except NotAcquired: print("Lock already expired or was reset externally.") ``` ```python # Release a lock acquired in another process using its known ID lock1 = redis_lock.Lock(conn, "shared-lock", expire=60) lock1.acquire() known_id = lock1.id # Pass this ID to another process # In another process / later: lock2 = redis_lock.Lock(conn, "shared-lock", id=known_id) lock2.release() # Releases using the matching ID ``` -------------------------------- ### Forcibly delete a specific Redis lock Source: https://context7.com/ionelmc/python-redis-lock/llms.txt The `lock.reset()` method forcibly deletes the lock's Redis key, releasing it unconditionally. This is primarily for crash recovery or administrative cleanup and does not check for ownership. ```python from redis import StrictRedis import redis_lock conn = StrictRedis() # Force-clear a stuck lock (e.g., after a crash) stale_lock = redis_lock.Lock(conn, "crashed-worker-lock") stale_lock.reset() print("Stale lock cleared.") ``` -------------------------------- ### Check if Lock is Locked Source: https://github.com/ionelmc/python-redis-lock/blob/master/docs/usage.md Check the current locked status of a given lock name. This does not acquire the lock, only checks its state. ```python is_locked = Lock(conn, "lock-name").locked() ``` -------------------------------- ### Lock.get_owner_id Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Retrieves the ID of the current lock owner. Returns the owner's ID string if the lock is held, or None if it is not. ```APIDOC ## Lock.get_owner_id — Retrieve the current lock owner's ID Returns the string ID stored in the Redis lock key, identifying which process/instance holds it. Returns `None` if the lock is not held. ### Method `get_owner_id() -> Optional[str]` ### Parameters None ### Request Example ```python from redis import StrictRedis import socket import redis_lock conn = StrictRedis() host_id = f"host:{socket.gethostname()}" lock = redis_lock.Lock(conn, "exclusive-job", id=host_id) lock.acquire() # From another part of the application: checker = redis_lock.Lock(conn, "exclusive-job") owner = checker.get_owner_id() if owner == host_id: print("This machine already holds the lock.") elif owner is not None: print(f"Lock is held by: {owner}") else: print("Lock is free.") lock.release() ``` ### Response - **owner_id** (str or None) - The ID of the lock owner, or None if the lock is not held. ``` -------------------------------- ### Extend Redis lock expiration time Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Use `lock.extend(expire=...)` to manually reset the TTL of an acquired lock. This is helpful for long-running jobs that might exceed their initial lock timeout. Raises `NotAcquired` if the lock is not held, `NotExpirable` if no expiry is set, and `ValueError` for negative expire values. ```python from redis import StrictRedis import redis_lock import time conn = StrictRedis() lock = redis_lock.Lock(conn, "batch-job", expire=10) lock.acquire() try: for i in range(5): print(f"Processing chunk {i}...") time.sleep(3) # Extend by another 10 seconds before expiry lock.extend(expire=10) print("Lock expiry extended.") finally: lock.release() ``` -------------------------------- ### Auto-renewing lock for long-running operations Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Use auto_renewal=True for locks that need to persist beyond a single operation. The lock will automatically renew its expiry before it runs out, preventing premature expiration during long tasks. ```python with redis_lock.Lock(conn, "long-job", expire=60, auto_renewal=True): print("Running a potentially long task — lock auto-renews every 40s.") time.sleep(120) # Lock will not expire during this sleep ``` -------------------------------- ### Lock.release Source: https://context7.com/ionelmc/python-redis-lock/llms.txt Releases the lock, signaling waiting callers and stopping auto-renewal. Raises NotAcquired if the lock was not held by this instance. ```APIDOC ## Lock.release — Release the lock Releases the lock. Signals any waiting `BLPOP` callers that the lock is free. Raises `NotAcquired` if the lock was not held by this instance (mismatched ID or already expired). Also stops the auto-renewal thread if one is running. ### Method `release()` ### Parameters None ### Request Example ```python from redis import StrictRedis import redis_lock from redis_lock import NotAcquired conn = StrictRedis() lock = redis_lock.Lock(conn, "my-resource", expire=30) lock.acquire() try: print("Doing work...") finally: try: lock.release() print("Lock released.") except NotAcquired: print("Lock already expired or was reset externally.") # Release a lock acquired in another process using its known ID lock1 = redis_lock.Lock(conn, "shared-lock", expire=60) lock1.acquire() known_id = lock1.id # Pass this ID to another process # In another process / later: lock2 = redis_lock.Lock(conn, "shared-lock", id=known_id) lock2.release() # Releases using the matching ID ``` ### Response None (raises exceptions on failure) ``` -------------------------------- ### Check if a Redis lock is held Source: https://context7.com/ionelmc/python-redis-lock/llms.txt The `lock.locked()` method returns `True` if the lock is currently held by any instance, regardless of the owner. This is useful for monitoring lock status or implementing conditional logic before attempting to acquire a lock. ```python from redis import StrictRedis import redis_lock conn = StrictRedis() lock = redis_lock.Lock(conn, "resource-x") print(lock.locked()) # False — no one holds it lock.acquire() print(lock.locked()) # True # Check from a completely separate Lock instance with the same name other = redis_lock.Lock(conn, "resource-x") print(other.locked()) # True — same Redis key is held lock.release() print(lock.locked()) # False ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.