fix: the search schedule logs its state when it changes, so an idle pass says why (M502 #5521)
release / govulncheck (push) Successful in 22s
release / web (push) Successful in 1m16s
release / go (push) Successful in 1m36s
release / integration (push) Successful in 4m53s
release / android (push) Successful in 4m54s
release / Build signed APK (releases and dev) (push) Successful in 5m4s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 16s
release / Verify release artifacts (tag releases only) (push) Skipped

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 09:30:26 -04:00
co-authored by Claude Opus 5.5
parent 7f1fe57935
commit 77081f1cef
2 changed files with 41 additions and 4 deletions
+24 -4
View File
@@ -89,6 +89,7 @@ type Report struct {
func (s *Searcher) Run(ctx context.Context) {
ticker := time.NewTicker(searchTick)
defer ticker.Stop()
said := ""
for {
select {
case <-ctx.Done():
@@ -96,14 +97,33 @@ func (s *Searcher) Run(ctx context.Context) {
case <-ticker.C:
}
rep := s.Pass(ctx)
if rep.Looked > 0 || rep.Error != "" {
s.logger.Info("wanted: search pass", "looked", rep.Looked, "grabbed", rep.Grabbed,
"waiting", rep.Waiting, "nothing", rep.Nothing, "failed", rep.Failed,
"paced", rep.Paced, "error", rep.Error)
// A pass that searched is always logged; an idle one only when the
// schedule's state changes, so the log says once why it is quiet.
if state := rep.state(); rep.Looked > 0 || rep.Error != "" || state != said {
s.logger.Info("wanted: search pass", "state", state, "looked", rep.Looked,
"grabbed", rep.Grabbed, "waiting", rep.Waiting, "nothing", rep.Nothing,
"failed", rep.Failed, "paced", rep.Paced, "error", rep.Error)
said = state
}
}
}
// state names why a pass did what it did: off, unconfigured, failed,
// idle (nothing was due) or searched.
func (r Report) state() string {
switch {
case r.Off:
return "off"
case r.Unconfigured:
return "unconfigured"
case r.Error != "":
return "failed"
case r.Looked == 0:
return "idle"
}
return "searched"
}
// Pass searches for the wants due, paced by the sources, until the window
// closes, and records what it did.
func (s *Searcher) Pass(ctx context.Context) Report {
+17
View File
@@ -194,3 +194,20 @@ func TestGrabs_OneLivePerWant_Integration(t *testing.T) {
t.Errorf("live = %+v %v %v", live, ok, err)
}
}
func TestReport_StateNamesWhyAPassWasQuiet(t *testing.T) {
for _, c := range []struct {
rep Report
want string
}{
{Report{Off: true}, "off"},
{Report{Unconfigured: true}, "unconfigured"},
{Report{Error: "boom"}, "failed"},
{Report{}, "idle"},
{Report{Looked: 2, Grabbed: 1}, "searched"},
} {
if got := c.rep.state(); got != c.want {
t.Errorf("%+v: state = %q, want %q", c.rep, got, c.want)
}
}
}