@@ -68,6 +68,7 @@ def __init__(self, lock_key: str):
6868 self .lock_key = lock_key
6969 self .lock_value = None
7070 self ._redis = None
71+ self .acquired_at = None # NEW: Track acquisition time
7172
7273 async def _get_redis (self ):
7374 if self ._redis is None :
@@ -77,33 +78,105 @@ async def _get_redis(self):
7778 async def __aenter__ (self ):
7879 redis_client = await self ._get_redis ()
7980 self .lock_value = str (uuid4 ())
81+ attempt_count = 0
82+ start_time = time .time ()
83+
84+ # NEW: Initial logging
85+ LOGGER .info (
86+ "Attempting to acquire lock '%s' with value '%s'" ,
87+ self .lock_key ,
88+ self .lock_value
89+ )
8090
81- # Keep trying to acquire the lock
8291 while True :
92+ attempt_count += 1
8393 acquired = await redis_client .set (
8494 self .lock_key ,
8595 self .lock_value ,
86- nx = True , # Only set if key doesn't exist
87- ex = 30 # 30s timeout
96+ nx = True ,
97+ ex = 30
8898 )
8999 if acquired :
100+ self .acquired_at = time .time ()
101+ # NEW: Success logging with metrics
102+ LOGGER .info (
103+ "Lock '%s' acquired successfully by '%s' after %d attempts in %.2f seconds" ,
104+ self .lock_key ,
105+ self .lock_value ,
106+ attempt_count ,
107+ self .acquired_at - start_time
108+ )
90109 break
91- await asyncio .sleep (0.1 )
110+
111+ # NEW: Timeout protection (prevents infinite waiting)
112+ elapsed = time .time () - start_time
113+ if elapsed > 25 : # 25 second timeout (5s buffer before Redis expires lock)
114+ LOGGER .error (
115+ "Failed to acquire lock '%s' after %.2f seconds and %d attempts - timeout exceeded" ,
116+ self .lock_key ,
117+ elapsed ,
118+ attempt_count
119+ )
120+ raise AnonCredsRevocationError (
121+ f"Timeout waiting for lock '{ self .lock_key } ' after { elapsed :.2f} seconds"
122+ )
123+
124+ # NEW: Periodic progress logging
125+ if attempt_count % 50 == 0 : # Log every 5 seconds
126+ LOGGER .warning (
127+ "Still waiting for lock '%s' after %d attempts (%.2f seconds)" ,
128+ self .lock_key ,
129+ attempt_count ,
130+ elapsed
131+ )
132+
133+ await asyncio .sleep (0.5 )
92134
93135 return self
94136
95137 async def __aexit__ (self , exc_type , exc_val , exc_tb ):
96138 if self .lock_value and self ._redis :
97- # Lua script to safely release only if we own the lock
98139 lua_script = """
99140 if redis.call("GET", KEYS[1]) == ARGV[1] then
100141 return redis.call("DEL", KEYS[1])
101142 else
102143 return 0
103144 end
104145 """
105- await self ._redis .eval (lua_script , 1 , self .lock_key , self .lock_value )
106- await self ._redis .close ()
146+ try :
147+ result = await self ._redis .eval (lua_script , 1 , self .lock_key , self .lock_value )
148+ held_duration = time .time () - self .acquired_at if self .acquired_at else 0
149+
150+ # NEW: Release logging with duration tracking
151+ if result == 1 :
152+ LOGGER .info (
153+ "Lock '%s' released successfully by '%s' after being held for %.2f seconds" ,
154+ self .lock_key ,
155+ self .lock_value ,
156+ held_duration
157+ )
158+ else :
159+ # NEW: Warning for expired locks
160+ LOGGER .warning (
161+ "Lock '%s' was already expired or released. Expected value '%s', held for %.2f seconds" ,
162+ self .lock_key ,
163+ self .lock_value ,
164+ held_duration
165+ )
166+ except Exception as e :
167+ # NEW: Error handling for release failures
168+ LOGGER .error (
169+ "Error releasing lock '%s' with value '%s': %s" ,
170+ self .lock_key ,
171+ self .lock_value ,
172+ str (e )
173+ )
174+ finally :
175+ try :
176+ await self ._redis .close ()
177+ LOGGER .info ("Redis connection closed for lock '%s'" , self .lock_key )
178+ except Exception as e :
179+ LOGGER .error ("Error closing Redis connection: %s" , str (e ))
107180
108181_credential_creation_lock = AsyncRedisLock ("acapy_credential_creation_lock" )
109182
0 commit comments