fix(miner): serialize per-repo base clone to prevent concurrent git races - #6917
Conversation
…aces ensureRepoCloned resolves one deterministic path per repoFullName and mutates it in place (git fetch/checkout/reset --hard) with no locking, while worktree-allocator only caps the total active-slot count and never enforces per-repo exclusivity. Two attempts for the same repo could therefore run these git commands against the shared base clone concurrently, corrupting the index/HEAD/refs or tripping .git/index.lock. Add a per-repoPath in-process async mutex: a Map of repoPath to the tail of an in-flight promise chain serializes same-repo ensureRepoCloned calls while different repoPaths still run in parallel. The lock is released on both success and failure (finally), so one failing attempt can neither reject a waiter nor wedge the queue, and the entry is dropped once the chain drains. Closes JSONbored#6762
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6917 +/- ##
=======================================
Coverage 93.70% 93.71%
=======================================
Files 685 685
Lines 68336 68348 +12
Branches 18724 18724
=======================================
+ Hits 64037 64049 +12
Misses 3302 3302
Partials 997 997
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
Tip ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-17 13:22:52 UTC
Review summary Nits — 3 non-blocking
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
Problem
ensureRepoCloned(packages/loopover-miner/lib/repo-clone.js) resolves one deterministic path perrepoFullNameand mutates it in place (git fetch origin,git checkout <base>,git reset --hard origin/<base>) with no locking.worktree-allocator.js'sacquire()only caps the total active-slot count (maxConcurrency), never per-repoFullNameexclusivity, so two attempts for the same repo can both hold a slot and callensureRepoClonedconcurrently — a genuine git-level race that corrupts the index/HEAD/refs or trips.git/index.lock.Fix
Add a per-
repoPathin-process async mutex.repoCloneLocksmaps a resolvedrepoPathto the tail of its in-flight promise chain, so same-repoensureRepoClonedcalls run strictly one after another while differentrepoPaths stay fully parallel. The publicensureRepoClonedresolves the samerepoPaththe git step uses as the mutex key and delegates the git work to an internalensureRepoClonedUnlocked. The lock is released in afinallyon both success and failure, so a failing attempt can neither reject a waiter nor wedge the queue; the map entry is dropped once the chain drains to keep it bounded.Tests
Added a #6762 regression suite:
cloneBaseDiris givenFull branch coverage on every changed line.
Closes #6762