|
@@ -79,6 +79,7 @@ class Lock:
|
|
|
self.last_refresh_dt = None
|
|
|
self.id = id or platform.get_process_id()
|
|
|
assert len(self.id) == 3
|
|
|
+ logger.debug(f"LOCK-INIT: initialising. store: {store}, stale: {stale}s, refresh: {stale // 2}s.")
|
|
|
|
|
|
def __enter__(self):
|
|
|
return self.acquire()
|
|
@@ -96,11 +97,13 @@ class Lock:
|
|
|
lock = dict(exclusive=exclusive, hostid=self.id[0], processid=self.id[1], threadid=self.id[2], time=timestamp)
|
|
|
value = json.dumps(lock).encode("utf-8")
|
|
|
key = bin_to_hex(xxh64(value))
|
|
|
+ logger.debug(f"LOCK-CREATE: creating lock in store. key: {key}, lock: {lock}.")
|
|
|
self.store.store(f"locks/{key}", value)
|
|
|
self.last_refresh_dt = now
|
|
|
return key
|
|
|
|
|
|
def _delete_lock(self, key, *, ignore_not_found=False):
|
|
|
+ logger.debug(f"LOCK-DELETE: deleting lock from store. key: {key}.")
|
|
|
try:
|
|
|
self.store.delete(f"locks/{key}")
|
|
|
except ObjectNotFound:
|
|
@@ -110,10 +113,10 @@ class Lock:
|
|
|
def _is_stale_lock(self, lock):
|
|
|
now = datetime.datetime.now(datetime.timezone.utc)
|
|
|
if lock["dt"] < now - self.stale_td:
|
|
|
- # lock is too old, it was not refreshed.
|
|
|
+ logger.debug(f"LOCK-STALE: lock is too old, it was not refreshed. lock: {lock}.")
|
|
|
return True
|
|
|
if not platform.process_alive(lock["hostid"], lock["processid"], lock["threadid"]):
|
|
|
- # we KNOW that the lock owning process is dead.
|
|
|
+ logger.debug(f"LOCK-STALE: we KNOW that the lock owning process is dead. lock: {lock}.")
|
|
|
return True
|
|
|
return False
|
|
|
|
|
@@ -151,6 +154,7 @@ class Lock:
|
|
|
# goal
|
|
|
# for exclusive lock: there must be only 1 exclusive lock and no other (exclusive or non-exclusive) locks.
|
|
|
# for non-exclusive lock: there can be multiple n-e locks, but there must not exist an exclusive lock.
|
|
|
+ logger.debug(f"LOCK-ACQUIRE: trying to acquire a lock. exclusive: {self.is_exclusive}.")
|
|
|
started = time.monotonic()
|
|
|
while time.monotonic() - started < self.timeout:
|
|
|
exclusive_locks = self._find_locks(only_exclusive=True)
|
|
@@ -165,27 +169,29 @@ class Lock:
|
|
|
exclusive_locks = self._find_locks(only_exclusive=True)
|
|
|
if self.is_exclusive:
|
|
|
if len(exclusive_locks) == 1 and exclusive_locks[0]["key"] == key:
|
|
|
- # success, we are the only exclusive lock! wait until the non-exclusive locks go away:
|
|
|
+ logger.debug("LOCK-ACQUIRE: we are the only exclusive lock!")
|
|
|
while time.monotonic() - started < self.timeout:
|
|
|
locks = self._find_locks(only_exclusive=False)
|
|
|
if len(locks) == 1 and locks[0]["key"] == key:
|
|
|
- # success, we are alone!
|
|
|
+ logger.debug("LOCK-ACQUIRE: success! no non-exclusive locks are left!")
|
|
|
return self
|
|
|
time.sleep(self.other_locks_go_away_delay)
|
|
|
+ logger.debug("LOCK-ACQUIRE: timeout while waiting for non-exclusive locks to go away.")
|
|
|
break # timeout
|
|
|
else:
|
|
|
- # take back our lock as some other client(s) also created exclusive lock(s).
|
|
|
+ logger.debug("LOCK-ACQUIRE: someone else also created an exclusive lock, deleting ours.")
|
|
|
self._delete_lock(key, ignore_not_found=True)
|
|
|
else: # not is_exclusive
|
|
|
if len(exclusive_locks) == 0:
|
|
|
- # success, noone else created an exclusive lock meanwhile!
|
|
|
+ logger.debug("LOCK-ACQUIRE: success! no exclusive locks detected.")
|
|
|
# We don't care for other non-exclusive locks.
|
|
|
return self
|
|
|
else:
|
|
|
- # take back our lock as some other client(s) also created exclusive lock(s).
|
|
|
+ logger.debug("LOCK-ACQUIRE: exclusive locks detected, deleting our shared lock.")
|
|
|
self._delete_lock(key, ignore_not_found=True)
|
|
|
# wait a random bit before retrying
|
|
|
time.sleep(self.retry_delay_min + (self.retry_delay_max - self.retry_delay_min) * random.random())
|
|
|
+ logger.debug("LOCK-ACQUIRE: timeout while trying to acquire a lock.")
|
|
|
raise LockTimeout(str(self.store))
|
|
|
|
|
|
def release(self):
|
|
@@ -193,7 +199,9 @@ class Lock:
|
|
|
if not locks:
|
|
|
raise NotLocked(str(self.store))
|
|
|
assert len(locks) == 1
|
|
|
- self._delete_lock(locks[0]["key"], ignore_not_found=True)
|
|
|
+ lock = locks[0]
|
|
|
+ logger.debug(f"LOCK-RELEASE: releasing lock: {lock}.")
|
|
|
+ self._delete_lock(lock["key"], ignore_not_found=True)
|
|
|
self.last_refresh_dt = None
|
|
|
|
|
|
def got_exclusive_lock(self):
|
|
@@ -202,6 +210,7 @@ class Lock:
|
|
|
|
|
|
def break_lock(self):
|
|
|
"""break ALL locks (not just ours)"""
|
|
|
+ logger.debug("LOCK-BREAK: break_lock() was called - deleting ALL locks!")
|
|
|
locks = self._get_locks()
|
|
|
for key in locks:
|
|
|
self._delete_lock(key, ignore_not_found=True)
|
|
@@ -209,6 +218,7 @@ class Lock:
|
|
|
|
|
|
def migrate_lock(self, old_id, new_id):
|
|
|
"""migrate the lock ownership from old_id to new_id"""
|
|
|
+ logger.debug(f"LOCK-MIGRATE: {old_id} -> {new_id}.")
|
|
|
assert self.id == old_id
|
|
|
assert len(new_id) == 3
|
|
|
old_locks = self._find_locks(only_mine=True)
|
|
@@ -234,10 +244,12 @@ class Lock:
|
|
|
# no archive has been added yet to the manifest, thus all objects looked unused/orphaned.
|
|
|
# another scenario when this can happen is a careless user running break-lock on another
|
|
|
# machine without making sure there is no borg activity in that repo.
|
|
|
- raise LockTimeout(str(self.store)) # our lock was killed, there is no safe way to continue.
|
|
|
+ logger.debug("LOCK-REFRESH: our lock was killed, there is no safe way to continue.")
|
|
|
+ raise LockTimeout(str(self.store))
|
|
|
assert len(old_locks) == 1 # there shouldn't be more than 1
|
|
|
old_lock = old_locks[0]
|
|
|
if old_lock["dt"] < now - self.refresh_td:
|
|
|
+ logger.debug(f"LOCK-REFRESH: lock needs a refresh. lock: {old_lock}.")
|
|
|
self._create_lock(exclusive=old_lock["exclusive"])
|
|
|
self._delete_lock(old_lock["key"])
|
|
|
self.last_refresh_dt = now
|