From e3a2c51c7e53003c7e1ed236a23696d57e643335 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 10 Sep 2026 15:57:51 +0200 Subject: [PATCH] bugfix: Check the mirror existence while holding the mirror lock The database was queried for the existence of the mirror before the mirror lock was aquired. Two gitcache processes starting with an empty cache both saw a missing mirror, so the process getting the lock as the second one deleted the mirror the first one had just created and cloned it again, breaking the operations of the other processes on that mirror. Query the database while holding the lock. Co-authored-by: Cursor --- CHANGELOG.md | 3 +++ src/git_cache/git_mirror.py | 9 +++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be77f67..1aa67ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Upcoming version +- Bugfix: Check the existence of a mirror while holding the mirror lock to no longer delete a mirror that was + created by a concurrent gitcache process. + ## v1.0.34 - Feature: Add optional detail and summary log files controlled by `GITCACHE_DETAIL_LOG` and `GITCACHE_SUMMARY_LOG` for CI-friendly invocation tracing. (thanks to ditschi) diff --git a/src/git_cache/git_mirror.py b/src/git_cache/git_mirror.py index 88d297d..aa8f3de 100644 --- a/src/git_cache/git_mirror.py +++ b/src/git_cache/git_mirror.py @@ -166,10 +166,9 @@ def update(self, ref=None, force=False): Return: Returns True if the mirror was updated or False if the request timed out. """ - mirror_exists = self.database.get(self.path) is not None try: with Locker(f"Mirror {self.path}", self.lockfile, self.config): - if not mirror_exists: + if self.database.get(self.path) is None: rmtree(self.path, ignore_errors=True) if self._clone(ref): record_cache("miss_create", self.path) @@ -202,10 +201,9 @@ def ensure_exists(self, ref=None) -> bool: Return: Returns True if the mirror exists now or False if the request timed out. """ - mirror_exists = self.database.get(self.path) is not None try: with Locker(f"Mirror {self.path}", self.lockfile, self.config): - if not mirror_exists: + if self.database.get(self.path) is None: rmtree(self.path, ignore_errors=True) if self._clone(ref): record_cache("miss_create", self.path) @@ -227,10 +225,9 @@ def fetch(self, command_args: List[str]) -> bool: Return: Returns True if the command was successfull, otherwise False. """ - mirror_exists = self.database.get(self.path) is not None try: with Locker(f"Mirror {self.path}", self.lockfile, self.config): - if not mirror_exists: + if self.database.get(self.path) is None: LOG.error("Mirror does not exist!") return False return self._fetch(command_args)