storelocking: check for other locks at least once before an exclusive acquire times out

This commit is contained in:
Mrityunjay Raj
2026-09-25 12:23:26 +05:30
parent 6df17a5c11
commit 461c3127f7
2 changed files with 17 additions and 1 deletions
+4 -1
View File
@@ -463,12 +463,15 @@ class Lock:
if self.is_exclusive:
if len(exclusive_locks) == 1 and exclusive_locks[0]["key"] == key:
logger.debug("LOCK-ACQUIRE: we are the only exclusive lock!")
while time.monotonic() - started < self.timeout:
# check at least once, even if creating our lock already used up the timeout.
while True:
locks = self._find_locks(only_exclusive=False)
if len(locks) == 1 and locks[0]["key"] == key:
logger.debug("LOCK-ACQUIRE: success! no non-exclusive locks are left!")
return self
blocking_locks = [lock for lock in locks if lock["key"] != key]
if time.monotonic() - started >= self.timeout:
break
self._log_blocking_locks(blocking_locks)
time.sleep(self.other_locks_go_away_delay)
logger.debug("LOCK-ACQUIRE: timeout while waiting for non-exclusive locks to go away.")
+13
View File
@@ -92,6 +92,19 @@ class TestLock:
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_exclusive_lock_on_slow_store(self, lockstore, monkeypatch):
# An exclusive lock is acquired if no other lock exists, even if writing our lock object
# takes longer than the timeout.
orig_store = lockstore.store
def slow_store(name, value):
time.sleep(0.2)
return orig_store(name, value)
monkeypatch.setattr(lockstore, "store", slow_store)
with Lock(lockstore, exclusive=True, id=ID1, timeout=0.1) as lock:
assert lock.got_exclusive_lock()
def test_double_nonexclusive_lock_succeeds(self, lockstore):
with Lock(lockstore, exclusive=False, id=ID1):
with Lock(lockstore, exclusive=False, id=ID2):