Require a positive RETENTION_SWEEP_INTERVAL (closes #140) #142
Reference in New Issue
Block a user
Delete Branch "issue-140-retention-sweep-interval-bound"
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?
Closes #140.
What changed
envDurationaccepted0sand negative values, andRETENTION_SWEEP_INTERVALfeedstime.NewTickerin both the retentionreaper (
internal/database/retention.go:122) and the archive sweeper(
internal/delivery/archive_sweeper.go:130).NewTickerpanics on anon-positive period and both tickers are created in goroutines with no
recover, so a bad value aborted the process after startup had already
logged "Configuration loaded".
envPositiveDurationininternal/config/config.go, structuredexactly like
envPortwrappingenvPositiveInt: it delegates toenvDurationand then rejects<= 0with an error that wrapsErrNonPositiveValueand names the variable. Same sentinel and samefailure shape as
PORTandRECEIVER_RATE_LIMIT.loadFromEnvuses it forRETENTION_SWEEP_INTERVAL.0sand-1hcases added toTestRetentionSweepInterval,asserting via the existing
expectStartupErrorForhelper that startupfails, that the message names
RETENTION_SWEEP_INTERVAL, and that theerror wraps
config.ErrNonPositiveValue.TestSessionIdleTimeoutasserting0sand-1hstill start successfully, so a future change cannot quietly turnthe documented "disabled" case into a startup failure.
RETENTION_SWEEP_INTERVALrow added to the env table (it wasmissing), and the "Invalid values abort startup" section now states the
positive requirement and calls out
SESSION_IDLE_TIMEOUTas thedeliberate exception.
envDurationauditThere are exactly two
envDurationcall sites in the tree:RETENTION_SWEEP_INTERVALtime.NewTickerininternal/database/retention.go:122andinternal/delivery/archive_sweeper.go:130SESSION_IDLE_TIMEOUTSESSION_IDLE_TIMEOUTonly ever becomessession.idleTimeout, which isguarded by an explicit
if s.idleTimeout <= 0 { return }at both of itsuse sites (
internal/session/session.go:264and:390). Non-positivethere means idle expiry is disabled, which is documented in the README,
so it stays on
envDuration.The remaining
NewTicker/AfterFuncsites take noenvironment-configurable duration:
internal/delivery/engine.go:619usesthe
retrySweepIntervalconstant,internal/delivery/engine.go:222usesa computed backoff delay, and the
context.WithTimeoutcalls ininternal/delivery/ssrf.go:146andinternal/server/server.go:166useconstants (and a non-positive timeout there would not panic in any case).
Gate evidence
make check— exit 0, tests executed (not cached):docker build --no-cache-filter=lint,builder --progress=plain .— exit 0,with both stages confirmed executed rather than
CACHED:No
parallel golangci-lint is runningin either run.TODO.mduntouched.PASS. Independently verified: the
envDurationaudit (exactly two call sites; the only otherNewTicker/AfterFunc/WithTimeoutsites take constants or a computed backoff), thatdefaultRetentionSweepIntervalalso passes the new bound so the unset path cannot reachNewTickerwith a non-positive period, thatloadFromEnvis the sole production constructor ofConfig, and that boths.idleTimeout <= 0guards (internal/session/session.go:264,:390) precede every use ofidleTimeout. Mutation-checked: reverting toenvDurationfails the two new subtests, and dropping the%wsentinel wrap alone also fails them, so neither assertion is vacuous.Gate (run locally on
500f39a, not trusting the cached CI log — #119):docker build --no-cache-filter=lint,builder --progress=plain .exit 0,lintstage executed (RUN make lint73.9s,0 issues., noparallel golangci-lint),builderRUN make testexecuted with zero(cached)markers and the four new subtests observed PASS in-container.Disclosure:
script/lintin this repo runs the hostgolangci-lint, so themake checklint result is not container evidence; the Dockerlintstage above is what I relied on.clawbot referenced this pull request2026-08-12 12:56:27 +02:00