Pin the pre-reopen close in archiveWriter.sweepExpired (unprotected connection-leak guard) #103
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up from the round-3 independent review of PR #95 (#89). Non-blocking there; the shipped behaviour is correct. Filing so the gap does not stay untracked.
The gap
internal/delivery/target_database_archive.go:369— thew.close()that runs before the reopen insweepExpiredhas no test behind it. Deleting it leaves the whole-racesuite green.The reviewer proved empirically what its absence costs, with a scratch probe that retained the pre-sweep
*sql.DBand pinged it after the sweep: without that close, one SQLite connection leaks per webhook per tick. At the default one-hourRETENTION_SWEEP_INTERVALthat is slow, but it is unbounded and scales with the number of archived webhooks, which is exactly the class of leak #89 was filed to close.This is the same category as the two round-2 blockers — a guard no test can distinguish from its own absence — and the review flagged that it considered blocking on it for consistency. It decided not to, on the grounds that the shipped code is correct, #89's definition of done is met, and unlike the round-2 blockers this line was never presented as a mutation-verified guard. I agree with that call, but the test should still exist.
Definition of done
internal/deliverythat retains the pre-sweep*sql.DBand assertsPing()errors afterSweepExpired.w.close()attarget_database_archive.go:369must make that test fail.make checkgreen via the repo's own entrypoints;.golangci.ymluntouched.Three smaller items from the same review, worth folding in
evictArchiveWriterIfUnusedininternal/handlers/source_management.go— thetype = databasefilter in the remaining-target count is unprotected: droppingAND type = ?leaves the suite green. Harm if it were wrong is bounded to behaviour the spec already accepts (an early eviction that a later delivery simply recreates), but the filter is load-bearing for intent and should be pinned.ArchiveSweeper.sweepissues its GORM query without a context. Matches the surrounding code, so not a regression — but see #102, which covers the broader "hook contexts are treated as decoration" theme in this codebase, and this belongs to the same cleanup.ArchiveSweeper.cancelis unsynchronised. Safe as currently wired and it mirrorsRetentionReaper, so this is consistency-only.Note on one mutation that survived correctly
The review also found that removing the
fileExistsstat insweepWebhookleaves the suite green — but verified this is correct, not a gap: witharchiveModeExisting(mode=rw) the stat is genuinely redundant belt-and-braces and its removal causes no behaviour change. No action needed; recorded so nobody re-discovers it and "fixes" it.