mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-08-03 23:32:31 +00:00
Pre-Alpha v?
Lost count: Added/Fixed bulk adding from stream Added/Fixed Auto assign channel numbers Added command to kill processes Changed to a persistant lock using redis
This commit is contained in:
parent
b1dcf159a2
commit
ace441fb56
15 changed files with 335 additions and 232 deletions
84
dispatcharr/persistent_lock.py
Normal file
84
dispatcharr/persistent_lock.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# dispatcharr/persistent_lock.py
|
||||
import uuid
|
||||
import redis
|
||||
|
||||
class PersistentLock:
|
||||
"""
|
||||
A persistent, auto-expiring lock that uses Redis.
|
||||
|
||||
Usage:
|
||||
1. Instantiate with a Redis client, a unique lock key (e.g. "lock:account:123"),
|
||||
and an optional timeout (in seconds).
|
||||
2. Call acquire() to try to obtain the lock.
|
||||
3. Optionally, periodically call refresh() to extend the lock's lifetime.
|
||||
4. When finished, call release() to free the lock.
|
||||
"""
|
||||
def __init__(self, redis_client: redis.Redis, lock_key: str, lock_timeout: int = 120):
|
||||
"""
|
||||
Initialize the lock.
|
||||
|
||||
:param redis_client: An instance of redis.Redis.
|
||||
:param lock_key: The unique key for the lock.
|
||||
:param lock_timeout: Time-to-live for the lock in seconds.
|
||||
"""
|
||||
self.redis_client = redis_client
|
||||
self.lock_key = lock_key
|
||||
self.lock_timeout = lock_timeout
|
||||
self.lock_token = None
|
||||
|
||||
def acquire(self) -> bool:
|
||||
"""
|
||||
Attempt to acquire the lock. Returns True if successful.
|
||||
"""
|
||||
self.lock_token = str(uuid.uuid4())
|
||||
# Set the lock with NX (only if not exists) and EX (expire time)
|
||||
result = self.redis_client.set(self.lock_key, self.lock_token, nx=True, ex=self.lock_timeout)
|
||||
return result is not None
|
||||
|
||||
def refresh(self) -> bool:
|
||||
"""
|
||||
Refresh the lock's expiration time if this instance owns the lock.
|
||||
Returns True if the expiration was successfully extended.
|
||||
"""
|
||||
current_value = self.redis_client.get(self.lock_key)
|
||||
if current_value and current_value.decode("utf-8") == self.lock_token:
|
||||
self.redis_client.expire(self.lock_key, self.lock_timeout)
|
||||
return True
|
||||
return False
|
||||
|
||||
def release(self) -> bool:
|
||||
"""
|
||||
Release the lock only if owned by this instance.
|
||||
Returns True if the lock was successfully released.
|
||||
"""
|
||||
# Use a Lua script for atomicity: only delete if the token matches.
|
||||
lua_script = """
|
||||
if redis.call("get", KEYS[1]) == ARGV[1] then
|
||||
return redis.call("del", KEYS[1])
|
||||
else
|
||||
return 0
|
||||
end
|
||||
"""
|
||||
release_lock = self.redis_client.register_script(lua_script)
|
||||
result = release_lock(keys=[self.lock_key], args=[self.lock_token])
|
||||
return result == 1
|
||||
|
||||
# Example usage (for testing purposes only):
|
||||
if __name__ == "__main__":
|
||||
# Connect to Redis on localhost; adjust connection parameters as needed.
|
||||
client = redis.Redis(host="localhost", port=6379, db=0)
|
||||
lock = PersistentLock(client, "lock:example_account", lock_timeout=120)
|
||||
|
||||
if lock.acquire():
|
||||
print("Lock acquired successfully!")
|
||||
# Do work here...
|
||||
# Optionally refresh the lock periodically:
|
||||
if lock.refresh():
|
||||
print("Lock refreshed.")
|
||||
# Finally, release the lock:
|
||||
if lock.release():
|
||||
print("Lock released.")
|
||||
else:
|
||||
print("Failed to release lock.")
|
||||
else:
|
||||
print("Failed to acquire lock.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue