Merge pull request #9871 from ThomasWaldmann/fix-lock-acquire-timeout

locking: do not leave a lock behind when exclusive acquire times out
This commit is contained in:
TW 2026-07-07 04:47:19 +02:00 committed by GitHub
commit 3539fccc64
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 14 additions and 0 deletions

View file

@ -189,6 +189,9 @@ class Lock:
return self
time.sleep(self.other_locks_go_away_delay)
logger.debug("LOCK-ACQUIRE: timeout while waiting for non-exclusive locks to go away.")
# we won't get the exclusive lock, so do not leave our lock behind:
# it would needlessly block other clients until it expired as stale.
self._delete_lock(key, ignore_not_found=True, update_last_refresh=True)
break # timeout
else:
logger.debug("LOCK-ACQUIRE: someone else also created an exclusive lock, deleting ours.")

View file

@ -45,6 +45,17 @@ class TestLock:
with pytest.raises(LockTimeout):
Lock(lockstore, exclusive=True, id=ID2).acquire()
def test_exclusive_lock_timeout_leaves_no_lock(self, lockstore):
# When acquiring an exclusive lock times out because a non-exclusive lock does not go away,
# the not-acquired exclusive lock must not stay behind in the store: it would block all
# other clients (even on other hosts) until it expired as stale.
with Lock(lockstore, exclusive=False, id=ID1) as shared_lock:
with pytest.raises(LockTimeout):
Lock(lockstore, exclusive=True, id=ID2).acquire()
locks = shared_lock._get_locks()
assert len(locks) == 1 # only the non-exclusive lock of ID1 is left
assert not any(lock["exclusive"] for lock in locks.values())
def test_double_nonexclusive_lock_succeeds(self, lockstore):
with Lock(lockstore, exclusive=False, id=ID1):
with Lock(lockstore, exclusive=False, id=ID2):