Problem:
When UnifiedLock.__aexit__ encountered an exception during async_lock.release(),
the error recovery logic would incorrectly attempt to release async_lock again
because it only checked main_lock_released flag. This could cause:
- Double-release attempts on already-failed locks
- Masking of original exceptions
- Undefined behavior in lock state
Root Cause:
The recovery logic used only main_lock_released to determine whether to attempt
async_lock release, without tracking whether async_lock.release() had already
been attempted and failed.
Fix:
- Added async_lock_released flag to track async_lock release attempts
- Updated recovery logic condition to check both main_lock_released AND
async_lock_released before attempting async_lock release
- This ensures async_lock.release() is only called once, even if it fails
Testing:
- Added test_aexit_no_double_release_on_async_lock_failure:
Verifies async_lock.release() is called only once when it fails
- Added test_aexit_recovery_on_main_lock_failure:
Verifies recovery logic still works when main lock fails
- All 5 UnifiedLock safety tests pass
Impact:
- Eliminates double-release bugs in multiprocess lock scenarios
- Preserves correct error propagation
- Maintains recovery logic for legitimate failure cases
Files Modified:
- lightrag/kg/shared_storage.py: Added async_lock_released tracking
- tests/test_unified_lock_safety.py: Added 2 new tests (5 total now pass)