Eviction no longer reads a whole table while requests wait on the database (closes #227)
check / check (push) Waiting to run

UsageBytes now reads the new cache_usage row, which triggers on
source_content and variant_content keep up to date in the statement
that adds, removes or resizes a row. The reconciliation pass reads both
tables 1000 rows per query, sums them, and corrects the total when it
differs, unless a row changed while it summed. Source rows now get
last_accessed_at when added, so choosing source images to evict reads
that column's index instead of sorting the whole table. Stats still
sums the tables, now in pages: an existing test drops both tables and
expects that sum to fail.

Model: opus-5-5
This commit is contained in:
2026-10-08 04:28:31 +00:00
parent 54328377d0
commit 7cbcd3957f
7 changed files with 612 additions and 88 deletions
+17 -3
View File
@@ -96,6 +96,17 @@ type Cache struct {
// deterministically pause inside that window to exercise
// concurrent stores against it; production code leaves it nil.
evictSourceBlobTestHook func(ContentHash)
// reconciliationPageSize is the most rows one read of a content table
// returns in the reconciliation pass and in Stats. newCache sets it to
// defaultReconciliationPageSize; tests set it smaller.
reconciliationPageSize int
// reconciliationReadTestHook, when set, is called after each of those
// reads with the number of rows the read covered, so tests can check
// that no read covers more than one page; production code leaves it
// nil.
reconciliationReadTestHook func(rows int)
}
// NewCache creates a new cache instance.
@@ -127,6 +138,8 @@ func newCache(
evictionDone: make(chan struct{}),
metaCache: metaCache,
contentLocks: newContentLock(),
reconciliationPageSize: defaultReconciliationPageSize,
}
if c.disabled {
@@ -471,8 +484,9 @@ func (c *Cache) Stats(ctx context.Context) (*CacheStats, error) {
return nil, fmt.Errorf("failed to get cache stats: %w", err)
}
// Count and size the cached source images and processed variants. A
// disabled cache holds none, whatever rows an earlier run left.
// Count and size the cached source images and processed variants from
// their tables. A disabled cache holds none, whatever rows an earlier
// run left.
if !c.disabled {
err = c.db.QueryRowContext(ctx, `
SELECT (SELECT COUNT(*) FROM source_content)
@@ -482,7 +496,7 @@ func (c *Cache) Stats(ctx context.Context) (*CacheStats, error) {
c.log.Warn("failed to count cache items for stats", "error", err)
}
stats.TotalSizeBytes, err = c.UsageBytes(ctx)
stats.TotalSizeBytes, err = c.sumContentSizeBytes(ctx)
if err != nil {
c.log.Warn("failed to sum cache size for stats", "error", err)
}