Conversation
…e lock Lock(timeout=0).reacquire() passed timeout=0 to the PEXPIRE-based Lua script; Redis >= 7.0 deletes the key on a 0 TTL, so the lock was silently destroyed while reacquire() returned True — mutual exclusion gone and the caller told everything was fine. The same guard that reacquire() already applies for timeout=None now covers timeout=0. Fixes redis#4279
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
Reviewed by Cursor Bugbot for commit ba7657d. Configure here.
| raise LockError( | ||
| "Cannot reacquire a lock with a timeout of 0", | ||
| lock_name=self.name, | ||
| ) |
There was a problem hiding this comment.
Async lock omits timeout guard
High Severity
The timeout <= 0 guard was added only on the sync Lock. Async do_reacquire still turns timeout=0 into PEXPIRE with 0 milliseconds, so reacquire() can delete the key, return success, and drop mutual exclusion for asyncio callers.
Reviewed by Cursor Bugbot for commit ba7657d. Configure here.
|
Hey @simpleqt, thank you for your contribution! The underlying bug in #4279 is real, but it is already being fixed in #4282, which enforces the invariant inside the Lua scripts instead: it covers Raising here would also change behavior in the other direction. Closing this one in favor of #4282. Your |


Fixes #4279
The bug
Lock(timeout=0).reacquire()rejectedtimeout is Nonebut lettimeout=0through.timeout=0→PEXPIRE key 0, which deletes the key (Redis ≥ 7.0), and the Lua script returned 1 — soreacquire()returnedTruewhile the lock was gone: mutual exclusion silently destroyed, the caller told everything was fine, and any other client could immediately acquire the same lock.Fix
do_reacquire()now raises the sameLockErrorthatreacquire()already raises fortimeout=Nonewhen the timeout is ≤ 0.Repro (executed with fakeredis + Lua)
Note
Medium Risk
Changes distributed lock renewal behavior for the edge case
timeout=0, but the new failure mode is safer than silently deleting the lock key.Overview
Fixes a silent mutual-exclusion break when
Lock(..., timeout=0).reacquire()ran after a successfulacquire().reacquire()already rejectedtimeout is None, buttimeout=0was allowed. That path converted the timeout toPEXPIRE key 0, which on Redis ≥ 7.0 removes the lock key while the Lua reacquire script still returned success—so callers gotTrueeven though the lock no longer existed.do_reacquire()now raisesLockError(same pattern as the no-timeout case) whentimeout <= 0, before touching Redis, so reacquire cannot report success after wiping the lock.Reviewed by Cursor Bugbot for commit ba7657d. Bugbot is set up for automated code reviews on this repo. Configure here.