Start no eviction pass after a stop, and end a pass quietly
check / check (push) Failing after 1m0s

Both pass runners do nothing once the loop's context is cancelled, so
a stop no longer starts an eviction pass after a cancelled
reconciliation or on a pending write-pressure wakeup or tick. In
evictBatch, a candidate that fails after cancellation ends the batch
with the context's error instead of logging its own warning; this
replaces the check before each candidate, since every candidate
started after cancellation fails at its first database call. A stop
now logs at most one warning.

Model: opus-5-5
This commit is contained in:
2026-10-04 05:50:43 +00:00
parent 742b6fa4c1
commit 12b4415c57
2 changed files with 23 additions and 10 deletions
+21 -9
View File
@@ -117,8 +117,10 @@ func (c *Cache) EvictToLimit(ctx context.Context) error {
// evictBatch fetches one batch of LRU candidates across variants and
// source blobs and evicts them oldest-first until excessBytes are
// freed or the batch is exhausted. It returns the bytes freed. Once ctx
// is cancelled, it stops at the next candidate and returns ctx's error.
// freed or the batch is exhausted. It returns the bytes freed. A
// candidate that fails once ctx is cancelled (every one started after
// that fails at its first database call) ends the batch with ctx's
// error, without a warning.
func (c *Cache) evictBatch(ctx context.Context, excessBytes int64) (int64, error) {
candidates, err := c.evictionCandidates(ctx)
if err != nil {
@@ -132,12 +134,12 @@ func (c *Cache) evictBatch(ctx context.Context, excessBytes int64) (int64, error
break
}
if ctx.Err() != nil {
return freed, ctx.Err()
}
err := c.evictCandidate(ctx, candidate)
if err != nil {
if ctx.Err() != nil {
return freed, ctx.Err()
}
c.log.Warn("failed to evict cache entry",
"cache_key", candidate.cacheKey,
"content_hash", candidate.contentHash,
@@ -463,7 +465,7 @@ func (c *Cache) StopEviction(ctx context.Context) error {
}
// evictionLoop is the body of the background eviction goroutine. It
// returns when ctx is cancelled.
// returns when ctx is cancelled, and starts no pass after that.
func (c *Cache) evictionLoop(ctx context.Context, interval time.Duration) {
defer close(c.evictionDone)
@@ -496,8 +498,13 @@ func (c *Cache) evictionLoop(ctx context.Context, interval time.Duration) {
}
// runEvictionPass runs one eviction pass, logging failures instead of
// propagating them (the loop must keep running).
// propagating them (the loop must keep running). It does nothing once
// ctx is cancelled.
func (c *Cache) runEvictionPass(ctx context.Context) {
if ctx.Err() != nil {
return
}
err := c.EvictToLimit(ctx)
if err != nil {
c.log.Warn("cache eviction pass failed", "error", err)
@@ -505,8 +512,13 @@ func (c *Cache) runEvictionPass(ctx context.Context) {
}
// runReconciliationPass runs one reconciliation pass, logging failures
// instead of propagating them (the loop must keep running).
// instead of propagating them (the loop must keep running). It does
// nothing once ctx is cancelled.
func (c *Cache) runReconciliationPass(ctx context.Context) {
if ctx.Err() != nil {
return
}
err := c.reconcileAccounting(ctx)
if err != nil {
c.log.Warn("cache accounting reconciliation failed", "error", err)