"""Small Redis lock helpers for background tasks.""" import uuid from dataclasses import dataclass from app.core.redis_sync import get_sync_redis @dataclass(frozen=True) class RedisLockHandle: key: str token: bytes def acquire_redis_lock(key: str, *, ttl_seconds: int) -> RedisLockHandle | None: """Acquire a single-owner Redis lock or return None when unavailable.""" client = get_sync_redis(decode_responses=False) token = uuid.uuid4().hex.encode("utf-8") if not client.set(key, token, nx=True, ex=ttl_seconds): return None return RedisLockHandle(key=key, token=token) def release_redis_lock(handle: RedisLockHandle | None) -> None: """Release the lock only if we still own it.""" if handle is None: return get_sync_redis(decode_responses=False).eval( """ if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) end return 0 """, 1, handle.key, handle.token, )