Skip to content

Commit ab2cd91

Browse files
committed
fix(core): remove KB rebuild lock that silently blocked first-run sync
The file-based lock in _check_and_run_pending_rebuild had a 300s stale threshold. Any subprocess that imported omnipkg and crashed before its finally block ran would leave a ghost lock file, silently skipping the KB rebuild for up to 5 minutes on every subsequent invocation — causing the "KB rebuild already in progress" warning with nothing actually running. Redis writes are atomic and idempotent. Two concurrent rebuilds produce identical results, so the lock bought nothing except the ability to get stuck. Redundant work on first-run is acceptable; a silently empty KB is not, especially since first-run can legitimately take 3+ minutes on large environments (safety install, full metadata scan, etc.). Also remove the dead second call to _check_and_run_pending_rebuild in _synchronize_knowledge_base_with_reality — result was discarded with `pass`, rebuild already runs at the top of that function.
1 parent 3df57c8 commit ab2cd91

1 file changed

Lines changed: 4 additions & 41 deletions

File tree

src/omnipkg/core.py

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7901,43 +7901,9 @@ def _check_and_run_pending_rebuild(self) -> bool:
79017901
except (json.JSONDecodeError, IOError):
79027902
return False
79037903

7904-
lock_file = self.config_manager.venv_path / ".omnipkg" / ".needs_kb_rebuild.lock"
7905-
_STALE_LOCK_SECONDS = 300
7906-
7907-
# ── Attempt to become the owner ──────────────────────────────────────
7908-
# We are the owner if EITHER:
7909-
# (a) no lock exists and we create it atomically, OR
7910-
# (b) the existing lock is stale and we replace it atomically.
7911-
#
7912-
# Use os.open with O_CREAT|O_EXCL for a true atomic create on all
7913-
# platforms (FileLock itself uses this internally). If the file already
7914-
# exists and is fresh, someone else is genuinely working — skip.
7915-
we_own_lock = False
7916-
try:
7917-
if lock_file.exists():
7918-
age = time.time() - lock_file.stat().st_mtime
7919-
if age <= _STALE_LOCK_SECONDS:
7920-
# Live process holds it — don't wait, just skip.
7921-
safe_print(_("⚠️ KB rebuild already in progress for {}. Skipping.").format(
7922-
current_version_str))
7923-
return False
7924-
# Stale — atomically replace it by deleting then exclusive-creating.
7925-
if lock_file.exists(): lock_file.unlink()
7926-
7927-
# Atomic exclusive create — fails if another process beat us here.
7928-
fd = os.open(str(lock_file), os.O_CREAT | os.O_EXCL | os.O_WRONLY)
7929-
os.write(fd, str(os.getpid()).encode())
7930-
os.close(fd)
7931-
we_own_lock = True
7932-
7933-
except FileExistsError:
7934-
# Another process won the race for the lock — skip silently.
7935-
return False
7936-
except OSError:
7937-
return False
7938-
7939-
if not we_own_lock:
7940-
return False
7904+
# No lock needed — Redis writes are atomic and idempotent.
7905+
# Two concurrent rebuilds produce identical results; redundant work
7906+
# is cheaper than a ghost lock silently blocking the rebuild for minutes.
79417907

79427908
# ── We own the lock — do the rebuild ─────────────────────────────────
79437909
try:
@@ -7980,8 +7946,7 @@ def _check_and_run_pending_rebuild(self) -> bool:
79807946
return False
79817947

79827948
finally:
7983-
# Always release our lock, even if rebuild raised an exception.
7984-
if lock_file.exists(): lock_file.unlink()
7949+
pass
79857950

79867951
def _repair_manifest_context_mismatch(
79877952
self, dist: importlib.metadata.Distribution, current_python_version: str
@@ -8318,8 +8283,6 @@ def _synchronize_knowledge_base_with_reality(
83188283

83198284
self._clean_corrupted_installs()
83208285
self._cleanup_all_cloaks_globally()
8321-
if self._check_and_run_pending_rebuild():
8322-
pass
83238286
# ── Fast missing-METADATA scan (main env + bubbles) ──────────────
83248287
if self._fast_missing_metadata_check():
83258288
safe_print(" 🔧 Auto-healed broken metadata, re-running discovery...")

0 commit comments

Comments
 (0)