// The periodic sweep must be able to SKIP a repo that provably has nothing to
// do — and must never skip one that does.
//
// Measured on the public backend (146 repos, 0.1.219): reconcileAll degraded the
// daemon from 11ms to ~450ms TTFB for ~57s of every 300s — a ~19% duty cycle, so
// about one request in five. `git` subprocess count was 0 for the whole window,
// so the cost is the per-tracker `listRefs()` (an `await base.update()` plus a
// full ref range-scan), not git plumbing. Yielding between repos (0.1.180)
// bounded a single stall but not the total, because the work itself was unchanged.
//
// The sweep is a BACKSTOP — per-repo reconcile is already event-driven off the
// tracker's `update` — so a materialized, quiet repo gains nothing from another
// pass. needsPass() decides, and it must fail SAFE: answer true whenever we
// cannot prove there is nothing to do.
import test from 'brittle'
import { mkdtempSync, writeFileSync } from '#node:fs'
import { join } from '#node:path'
import { tmpdir } from '#node:os'
import { RefReconciler } from '../lib/services/ref-reconciler.js'
import { execFileSync } from '../lib/_compat-exec.js'
function git (args, opts = {}) {
return execFileSync('git', args, { encoding: 'utf8', ...opts }).toString().trim()
}
// Tracker whose linearized view version we can advance, the way replication does.
function fakeTracker (refs, { version = 1, withVersion = true } = {}) {
const t = {
base: {
on () {},
off () {},
view: withVersion ? { version } : {}
},
async listRefs () { return t._refs.slice() },
_refs: refs.slice(),
setVersion (v) { if (t.base.view) t.base.view.version = v }
}
return t
}
async function makeRepo () {
const dir = mkdtempSync(join(tmpdir(), 'pgv3-skip-'))
const bare = join(dir, 'repo.git')
const work = join(dir, 'work')
git(['init', '--bare', '-q', bare])
git(['init', '-q', work])
git(['-C', work, 'config', 'user.email', 't@t'])
git(['-C', work, 'config', 'user.name', 'T'])
git(['-C', work, 'config', 'commit.gpgsign', 'false'])
writeFileSync(join(work, 'a.txt'), 'one\n')
git(['-C', work, 'add', '.'])
git(['-C', work, 'commit', '-q', '-m', 'c1'])
const oid = git(['-C', work, 'rev-parse', 'HEAD'])
git(['-C', work, 'push', '-q', bare, 'HEAD:refs/heads/main'])
return { bare, oid }
}
test('a reconciler that has never passed always needs one', async (t) => {
const { bare, oid } = await makeRepo()
const rec = new RefReconciler({ tracker: fakeTracker([{ ref: 'refs/heads/main', hash: oid }]), repoPath: bare })
t.ok(rec.needsPass(), 'no pass has completed yet, so nothing is proven')
})
test('a materialized, quiet repo does NOT need another pass', async (t) => {
const { bare, oid } = await makeRepo()
const rec = new RefReconciler({ tracker: fakeTracker([{ ref: 'refs/heads/main', hash: oid }]), repoPath: bare })
await rec.reconcile()
t.absent(rec.needsPass(), 'view unchanged + nothing deferred => the sweep can skip it')
})
test('an advancing view re-arms the sweep', async (t) => {
const { bare, oid } = await makeRepo()
const tracker = fakeTracker([{ ref: 'refs/heads/main', hash: oid }])
const rec = new RefReconciler({ tracker, repoPath: bare })
await rec.reconcile()
t.absent(rec.needsPass(), 'quiet after the pass')
tracker.setVersion(2) // a peer's write replicated in
t.ok(rec.needsPass(), 'the view advanced, so there is work to do')
})
// The safety case. A ref whose objects never arrived is exactly the state the
// sweep exists to heal, so a quiet view must NOT be enough to skip it.
test('a ref left unmaterialized keeps the repo in the sweep', async (t) => {
const { bare } = await makeRepo()
const missing = 'd'.repeat(40) // a well-formed oid that is not in the repo
const rec = new RefReconciler({
tracker: fakeTracker([{ ref: 'refs/heads/orphan', hash: missing }]),
repoPath: bare
})
await rec.reconcile()
// rev-parse exits non-zero for an absent ref, which the helper turns into a throw.
let materialized = true
try { git(['--git-dir', bare, 'rev-parse', '--verify', 'refs/heads/orphan']) } catch { materialized = false }
t.absent(materialized, 'the ref was not materialized (its object is absent)')
t.ok(rec.needsPass(), 'deferred work outranks a quiet view — the sweep must retry')
})
test('an unreadable view version never allows a skip', async (t) => {
const { bare, oid } = await makeRepo()
const rec = new RefReconciler({
tracker: fakeTracker([{ ref: 'refs/heads/main', hash: oid }], { withVersion: false }),
repoPath: bare
})
await rec.reconcile()
t.ok(rec.needsPass(), 'no usable change signal => fail safe and do the work')
})
test('the backstop deadline re-arms an otherwise quiet repo', async (t) => {
const { bare, oid } = await makeRepo()
const rec = new RefReconciler({
tracker: fakeTracker([{ ref: 'refs/heads/main', hash: oid }]),
repoPath: bare,
backstopMs: 0 // every pass is immediately overdue
})
await rec.reconcile()
t.ok(rec.needsPass(), 'silent drift still heals because the backstop expires')
})