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
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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user