From 8fc1d72cd546817dba02a089608016cff6a5feb8 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 9 Oct 2026 20:04:11 +0200 Subject: [PATCH] Keep the setuid search's time on the caller's clock SetuidCache compared the caller's now with a time read from the wall clock when the search finished. When the caller's clock ran behind the wall clock, a search was never due again: TestTheSetuidSearchRunsInTheBackgroundAndIsKeptForItsInterval, whose clock is a fixed date, has failed on main and every branch since 2026-10-09 18:00 UTC. A search's time is now the now it was started at; a result reads at most one search older than it is, which errs towards searching again. --- internal/accounts/ways.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/internal/accounts/ways.go b/internal/accounts/ways.go index 9aa525c..122b0cf 100644 --- a/internal/accounts/ways.go +++ b/internal/accounts/ways.go @@ -390,7 +390,7 @@ type SetuidCache struct { mu sync.Mutex wg sync.WaitGroup - at time.Time // when the last search that finished finished + at time.Time // when the last search that finished was started, on the caller's clock found []string running bool startedAt time.Time @@ -431,8 +431,14 @@ const ( ReasonIncomplete = rootsearch.ReasonIncomplete ) -// Look answers the last result and when its search finished, starting a search in the background when one is +// Look answers the last result and when its search was started, starting a search in the background when one is // due; it never waits for one. Nil c searches in place, for a caller that wants to. +// +// **One clock: the caller's.** A search's time is the `now` it was started at, never the wall clock read when it +// finished: due-ness compares a later `now` with it, and mixing the two clocks made a search never due again +// whenever the caller's clock ran behind the wall clock — the test with a fixed date failed on every branch from +// 2026-10-09 18:00 UTC (found while checking issue 356). Measured from the start, a result reads at most one +// search older than it is, which errs towards searching again. func (c *SetuidCache) Look(now time.Time, search func(ctx context.Context) ([]string, error)) ([]string, time.Time, error) { if c == nil { ctx, cancel := context.WithTimeout(context.Background(), SearchBound) @@ -446,6 +452,7 @@ func (c *SetuidCache) Look(now time.Time, search func(ctx context.Context) ([]st (c.failed == nil || now.Sub(c.failedAt) >= c.backoff) if due { c.running, c.startedAt = true, now + started := now c.wg.Add(1) go func() { defer c.wg.Done() @@ -455,13 +462,12 @@ func (c *SetuidCache) Look(now time.Time, search func(ctx context.Context) ([]st c.mu.Lock() defer c.mu.Unlock() c.running = false - done := time.Now() if err != nil { - c.failed, c.failedAt = err, done + c.failed, c.failedAt = err, started c.backoff = min(max(2*c.backoff, c.Every), MaxBackoff) return } - c.found, c.at, c.failed, c.backoff = found, done, nil, 0 + c.found, c.at, c.failed, c.backoff = found, started, nil, 0 }() } switch {