fix(backfill): scheduled per-repo backfill respects freshness/error-backoff - #4529
Conversation
…ackoff enqueueRepositoryOpenDataBackfill is the function the real scheduled cron path routes through, but it had no freshness/error-backoff check at all -- every registered repo got a full 4-segment GitHub re-sync every 30 minutes forever, and a repo stuck in status:error got retried every 30 min instead of backing off for 1 hour. The correct check already existed in backfillRegisteredRepositories but that function is only reached via a synchronous admin endpoint or requestedBy==='test', never the scheduled path. Extracts the freshness/backoff decision into a shared syncFreshnessSkipReason helper both functions now call. Closes #4497.
|
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 #4529 +/- ##
=======================================
Coverage 94.04% 94.04%
=======================================
Files 422 422
Lines 37574 37580 +6
Branches 13724 13727 +3
=======================================
+ Hits 35335 35342 +7
Misses 1583 1583
+ Partials 656 655 -1
🚀 New features to boost your workflow:
|
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-09 23:20:12 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 7 non-blocking
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🟩 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 Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Codecov flagged a partial branch: the never_synced status value (a row exists but no sync ever completed) and the stale-syncState-proceeds path (past both the fresh-success and error-backoff windows) were never independently exercised. Adds both cases.
…ock every job bumps (#10203) enqueueRepositoryOpenDataBackfill measured freshness on repo_sync_state.lastCompletedAt, but refreshRepoSyncStateFromSegments rewrites that repo-wide column at the tail of EVERY segment write path -- including the ~2-minute re-gate sweep's own force:true open_pull_requests refresh and the backfill-pr-details follow-on it enqueues, neither of which dispatches any open-data work. The clock therefore never aged past FRESH_SYNC_MS and the gate never opened: labels, open_issues and recent_merged_pull_requests went undispatched from the moment #4529 shipped (2026-07-09T23:27Z, sixty-eight seconds after the last crawl completed). Only recent_merged_pull_requests showed it. The other three open-data tables have webhook or sweep writers that kept them current and masked the dead crawl, so the table quietly served a three-week-old window to the copycat containment engine, the repo culture profile (whose cache-invalidation signal is the frozen row count, so it never regenerated at all), the maintainer recap, six services, and the public API and MCP surfaces. Freshness now reads the oldest completion across the four segments the fan-out actually dispatches, treating a never-run segment as infinitely old. That measures attempts rather than successes, so #4497's throttle is intact: a repo that genuinely completed a crawl still backs off for six hours. The error-backoff window keeps reading lastCompletedAt -- a repo-level error state is repo-wide by nature. The skip was also invisible: it was reported only as a warnings[] string that the cron caller discards, so three weeks of a starved crawl left no log, metric, or audit trail. It now emits a counter and a log line with the crawl age, and listContributorRecentMergedPullRequests -- a reader with no production callers -- is removed. Closes #10193.
Closes #4497.
Summary
enqueueRepositoryOpenDataBackfillis the function the real scheduled cron path routes through (processors.ts:1021-1029, when a per-repobackfill-registered-reposmessage hasrepoFullNameset andrequestedBy !== "test"), but it had no freshness/error-backoff check at all — every registered repo got a full 4-segment GitHub re-sync every 30 minutes forever, and a repo stuck instatus: "error"got retried every 30 min instead of backing off for 1 hour.backfillRegisteredRepositories, but that function is only reached in production via the synchronous/v1/internal/jobs/backfill-registered-repos/runadmin endpoint orrequestedBy === "test"— never the scheduled path.syncFreshnessSkipReason(a pure helper) and wired it into both functions, checked BEFORE any GitHub/DB work inenqueueRepositoryOpenDataBackfill(moved thegetRepoSyncStateread earlier so a skip avoids the totals fetch too, not just the segment enqueue).src/index.ts:113's comment about "backfillRegisteredRepositories's freshness window" no longer needed correcting — it's now genuinely accurate for both paths since they share the same check.Scope
type(scope): short summaryConventional Commit format.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
npm run typechecksrc/github/backfill.tslocally, confirmed all 3 new tests fail with the exact old behavior) and PASS with it.npx vitest run test/unit/backfill.test.ts test/unit/queue.test.ts test/unit/backfill-file-hydration-scoping.test.ts(993 passed)npm run test:coveragenot re-run unsharded locally for this diff; relying on CI's full gate given the change's narrow blast radius (one shared helper + two call sites already heavily covered by the 993 tests above).If any required check was skipped, explain why:
Safety