fix(jobs): timer leak in Module.Stop and warn on unknown runner names

Module.Stop used time.After which leaks the timer if the done channel
wins first; switch to time.NewTimer + defer Stop() to align with the
codebase convention.

scheduleEntry silently returned {0, false} for unknown runner names —
typos in production wiring were invisible. Return (entry, known) and
log Warn at NewModule registration time so misconfiguration surfaces.
This commit is contained in:
2026-05-06 08:37:09 +08:00
parent 895a816221
commit 895799a6c9
+14 -9
View File
@@ -47,7 +47,10 @@ func NewModule(cfg Config, deps ModuleDeps) *Module {
sch := NewScheduler(log)
for name, fn := range deps.RunnerFns {
entry := scheduleEntry(cfg, name)
entry, known := scheduleEntry(cfg, name)
if !known {
log.Warn("jobs.module.unknown_runner", "name", name)
}
sch.Add(name, entry.interval, entry.enabled, fn)
}
@@ -95,10 +98,12 @@ func (m *Module) Stop(ctx context.Context) {
if grace <= 0 {
grace = 30 * time.Second
}
timer := time.NewTimer(grace)
defer timer.Stop()
select {
case <-done:
m.log.Info("jobs.module.stopped")
case <-time.After(grace):
case <-timer.C:
m.log.Warn("jobs.module.shutdown_timeout", "grace_ms", grace.Milliseconds())
case <-ctx.Done():
m.log.Warn("jobs.module.shutdown_ctx_canceled")
@@ -112,18 +117,18 @@ type schedEntry struct {
enabled bool
}
func scheduleEntry(cfg Config, name string) schedEntry {
func scheduleEntry(cfg Config, name string) (schedEntry, bool) {
switch name {
case "workspace_fetch":
return schedEntry{cfg.WorkspaceFetch.Interval, cfg.WorkspaceFetch.Enabled}
return schedEntry{cfg.WorkspaceFetch.Interval, cfg.WorkspaceFetch.Enabled}, true
case "worktree_prune":
return schedEntry{cfg.WorktreePrune.Interval, cfg.WorktreePrune.Enabled}
return schedEntry{cfg.WorktreePrune.Interval, cfg.WorktreePrune.Enabled}, true
case "attachment_cleanup":
return schedEntry{cfg.AttachmentCleanup.Interval, cfg.AttachmentCleanup.Enabled}
return schedEntry{cfg.AttachmentCleanup.Interval, cfg.AttachmentCleanup.Enabled}, true
case "job_reaper":
return schedEntry{cfg.JobReaper.Interval, cfg.JobReaper.Enabled}
return schedEntry{cfg.JobReaper.Interval, cfg.JobReaper.Enabled}, true
case "job_purge":
return schedEntry{cfg.JobPurge.Interval, cfg.JobPurge.Enabled}
return schedEntry{cfg.JobPurge.Interval, cfg.JobPurge.Enabled}, true
}
return schedEntry{0, false}
return schedEntry{0, false}, false
}