Skip to content

Commit

Permalink
LockRoster.modify: no KeyError if element was already gone, fixes #7937
Browse files Browse the repository at this point in the history
The intention of LockRoster.modify(key, REMOVE) is to remove self.id.

Using set.discard will just ignore it if self.id is not present there anymore.

Previously, using set.remove triggered a KeyError that has been frequently
seen in tracebacks of teardowns involving Repository.__del__ and Repository.__exit__.

I added a REMOVE2 op to serve one caller that needs to get the KeyError if
self.id was not present.

Thanks to @herrmanntom for the workaround!
  • Loading branch information
ThomasWaldmann committed Nov 18, 2023
1 parent 7c20bb5 commit 046f9fe
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 3 deletions.
9 changes: 7 additions & 2 deletions src/borg/locking.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .helpers import Error, ErrorWithTraceback
from .logger import create_logger

ADD, REMOVE = "add", "remove"
ADD, REMOVE, REMOVE2 = "add", "remove", "remove2"
SHARED, EXCLUSIVE = "shared", "exclusive"

logger = create_logger(__name__)
Expand Down Expand Up @@ -326,6 +326,11 @@ def modify(self, key, op):
if op == ADD:
elements.add(self.id)
elif op == REMOVE:
# note: we ignore it if the element is already not present anymore.
# this has been frequently seen in teardowns involving Repository.__del__ and Repository.__exit__.
elements.discard(self.id)
elif op == REMOVE2:
# needed for callers that do not want to ignore.
elements.remove(self.id)
else:
raise ValueError("Unknown LockRoster op %r" % op)
Expand All @@ -340,7 +345,7 @@ def migrate_lock(self, key, old_id, new_id):
killing, self.kill_stale_locks = self.kill_stale_locks, False
try:
try:
self.modify(key, REMOVE)
self.modify(key, REMOVE2)
except KeyError:
# entry was not there, so no need to add a new one, but still update our id
self.id = new_id
Expand Down
2 changes: 1 addition & 1 deletion src/borg/testsuite/locking.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def test_kill_stale(self, lockpath, free_pid):
assert roster.get(SHARED) == {our_id}
assert roster.get(EXCLUSIVE) == set()
assert roster.get(SHARED) == set()
with pytest.raises(KeyError):
with pytest.raises(NotLocked):
dead_lock.release()

with Lock(lockpath, id=cant_know_if_dead_id, exclusive=True):
Expand Down

0 comments on commit 046f9fe

Please sign in to comment.