fix(miner): enforce maxConcurrentClaims atomically across sibling processes - #6912
Conversation
…cesses The cap was check-then-act: attempt-cli.js read the active-claim count via listActiveClaims, then recorded the claim in a separate claimLedger.claimIssue call with no shared transaction. Two sibling miner processes racing the same repo could both pass the sub-cap count check before either committed, both claim, and exceed maxConcurrentClaims. Add claimIssueWithinCap to the claim ledger: it sweeps orphaned claims, counts the repo active claims, and records the new claim only while under the cap, all inside one BEGIN IMMEDIATE transaction. With node:sqlite shared busy_timeout the loser waits for the winner commit, sees the committed count, and is cleanly rejected (claimed: false) so the caller still logs the cap violation. attempt-cli.js now calls this instead of the split check-then-claim. Closes JSONbored#6758
|
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 #6912 +/- ##
=======================================
Coverage 93.70% 93.70%
=======================================
Files 685 685
Lines 68313 68331 +18
Branches 18723 18723
=======================================
+ Hits 64014 64032 +18
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 12:39:13 UTC
Review summary Nits — 5 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.
|
Context
maxConcurrentClaimswas enforced as check-then-act:attempt-cli.jsread the active-claim count viaclaimLedger.listActiveClaims(...), then recorded the claim in a separateclaimLedger.claimIssue(...)call with no shared transaction.claimIssueonly didsweepExpiredClaims+recordClaim, andrecordClaimis atomic only per-issue-uniqueness (INSERT ... ON CONFLICT), never a repo-wide cap. Two sibling miner processes racing the same repo could both read the same sub-cap count before either committed, both claim, and exceed the cap.Fix
claim-ledger.js: newclaimIssueWithinCap(repoFullName, issueNumber, note, apiBaseUrl, maxConcurrentClaims)that sweeps orphaned claims, counts the repo active claims, and records the new claim only while strictly under the cap, all inside oneBEGIN IMMEDIATEtransaction. Withnode:sqlitesharedbusy_timeout, the loser waits for the winner commit, sees the committed count, and is cleanly rejected withclaimed: false(never silently dropped) so the caller still logs the cap violation.attempt-cli.js: calls the atomic count-and-claim instead of the splitlistActiveClaimspre-check + separateclaimIssue. The blocked path (log/event/JSON/human message/exit 11) is byte-for-byte unchanged; only itsactiveClaimCountsource moved to the atomic result.Tests
miner-claim-ledger.test.ts: under-cap success, at-cap rejection, per-repo scoping, two sibling connections to one DB file racing the cap (only one wins), sweep-frees-a-slot inside the transaction, invalid-cap rejection, and transaction rollback on a mid-transaction throw.miner-attempt-cli.test.ts: existing cap-enforcement cases updated to the atomic entry point.100% patch line + branch coverage on the changed source.
Closes #6758