@@ -321,20 +321,22 @@ async def safe_execute(stmt, params=None, max_retries: int = 2):
321321 # Session auto-closed by context manager, locks released
322322
323323 # Determine error type for retry logic
324- is_mysql_deadlock = (
325- hasattr (err , "orig" )
326- and hasattr (err .orig , "args" )
327- and len (err .orig .args ) > 0
328- and err .orig .args [0 ] == 1213
324+ mysql_errno = (
325+ err .orig .args [0 ]
326+ if hasattr (err , "orig" ) and hasattr (err .orig , "args" ) and len (err .orig .args ) > 0
327+ else None
329328 )
329+ # 1213 = deadlock, 1205 = lock wait timeout
330+ is_mysql_retriable = mysql_errno in (1213 , 1205 )
330331 is_pg_deadlock = hasattr (err , "orig" ) and hasattr (err .orig , "code" ) and err .orig .code == "40P01"
331332 is_sqlite_locked = "database is locked" in str (err )
332333
333334 # Retry with exponential backoff if retriable error
334335 if attempt < max_retries - 1 :
335- if is_mysql_deadlock or is_pg_deadlock :
336+ if is_mysql_retriable or is_pg_deadlock :
336337 # Exponential backoff with jitter: 50-75ms, 100-150ms
337- base_delay = 0.05 * (2 ** attempt )
338+ # Use longer base delay for lock wait timeouts vs deadlocks
339+ base_delay = 0.1 * (2 ** attempt ) if mysql_errno == 1205 else 0.05 * (2 ** attempt )
338340 jitter = random .uniform (0 , base_delay * 0.5 )
339341 await asyncio .sleep (base_delay + jitter )
340342 continue
0 commit comments