Keep the setuid search's time on the caller's clock
mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery failed: its walk failed: a gate on a first machine (what it carried put back), a build, a machine

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.
This commit is contained in:
jochen
2026-10-09 20:04:11 +02:00
parent 9a807de659
commit 8fc1d72cd5
+11 -5
View File
@@ -390,7 +390,7 @@ type SetuidCache struct {
mu sync.Mutex mu sync.Mutex
wg sync.WaitGroup 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 found []string
running bool running bool
startedAt time.Time startedAt time.Time
@@ -431,8 +431,14 @@ const (
ReasonIncomplete = rootsearch.ReasonIncomplete 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. // 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) { func (c *SetuidCache) Look(now time.Time, search func(ctx context.Context) ([]string, error)) ([]string, time.Time, error) {
if c == nil { if c == nil {
ctx, cancel := context.WithTimeout(context.Background(), SearchBound) 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) (c.failed == nil || now.Sub(c.failedAt) >= c.backoff)
if due { if due {
c.running, c.startedAt = true, now c.running, c.startedAt = true, now
started := now
c.wg.Add(1) c.wg.Add(1)
go func() { go func() {
defer c.wg.Done() defer c.wg.Done()
@@ -455,13 +462,12 @@ func (c *SetuidCache) Look(now time.Time, search func(ctx context.Context) ([]st
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
c.running = false c.running = false
done := time.Now()
if err != nil { 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) c.backoff = min(max(2*c.backoff, c.Every), MaxBackoff)
return 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 { switch {