test: add failing test for one-shot-only reconciliation
reconcileAccounting currently runs exactly once, at evictor startup. Combined with StoreVariant's best-effort accounting insert, a variant file that lands on disk untracked during steady-state operation (e.g. insert failed under transient DB contention) stays invisible to UsageBytes/EvictToLimit until the next process restart -- the drift window the review flagged. Currently red: a file introduced after startup reconciliation has already run is never adopted.
This commit is contained in:
@@ -668,6 +668,71 @@ func TestStartEvictionReconcilesAccountingWithDisk(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPeriodicReconciliationAdoptsFileThatAppearsAfterStartup proves
|
||||||
|
// reconciliation is not a one-shot startup-only pass: it must also run
|
||||||
|
// on the periodic ticker, so a variant file that lands on disk with no
|
||||||
|
// accounting row well after startup (e.g. because StoreVariant's
|
||||||
|
// best-effort accounting insert failed under transient contention, or
|
||||||
|
// any other cause of an untracked file appearing during steady-state
|
||||||
|
// operation) is still adopted into accounting eventually, rather than
|
||||||
|
// staying invisible to UsageBytes/EvictToLimit until the next process
|
||||||
|
// restart.
|
||||||
|
func TestPeriodicReconciliationAdoptsFileThatAppearsAfterStartup(t *testing.T) {
|
||||||
|
cache, _ := newEvictionTestCache(t, 1<<30)
|
||||||
|
|
||||||
|
const interval = 100 * time.Millisecond
|
||||||
|
|
||||||
|
cache.StartEviction(interval)
|
||||||
|
defer cache.StopEviction()
|
||||||
|
|
||||||
|
// Let startup reconciliation run and settle on an empty cache
|
||||||
|
// before introducing the untracked file, so the adoption we assert
|
||||||
|
// below can only be the work of a later, periodic pass.
|
||||||
|
time.Sleep(3 * interval)
|
||||||
|
|
||||||
|
// Simulate a variant whose accounting insert failed after the
|
||||||
|
// process was already running and serving requests: the content
|
||||||
|
// file is written directly, bypassing StoreVariant's (and thus its
|
||||||
|
// accounting insert) entirely, exactly as would happen if that
|
||||||
|
// insert had failed and only the file write had succeeded.
|
||||||
|
untracked := bytes.Repeat([]byte{0x41}, 900)
|
||||||
|
if _, err := cache.variants.Store("aabbccdd0099", bytes.NewReader(untracked), "image/webp"); err != nil {
|
||||||
|
t.Fatalf("failed to store untracked variant file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deadline := time.Now().Add(5 * time.Second)
|
||||||
|
|
||||||
|
var usage int64
|
||||||
|
|
||||||
|
for time.Now().Before(deadline) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
usage, err = cache.UsageBytes(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("UsageBytes failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if usage == 900 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(25 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
if usage != 900 {
|
||||||
|
t.Errorf("usage after periodic reconciliation = %d, want 900 "+
|
||||||
|
"(a file that appeared after startup reconciliation already ran must still "+
|
||||||
|
"be adopted by a later periodic pass)", usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n := countRows(t, cache,
|
||||||
|
`SELECT COUNT(*) FROM variant_content WHERE cache_key = ?`, "aabbccdd0099",
|
||||||
|
); n != 1 {
|
||||||
|
t.Errorf("file that appeared after startup was not adopted by periodic "+
|
||||||
|
"reconciliation (rows=%d)", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestEvictSourceBlobExcludesConcurrentStoreOfIdenticalContent exercises
|
// TestEvictSourceBlobExcludesConcurrentStoreOfIdenticalContent exercises
|
||||||
// the exact TOCTOU window between evictSourceBlob's row-deletion
|
// the exact TOCTOU window between evictSourceBlob's row-deletion
|
||||||
// transaction commit and its content file unlink: a concurrent
|
// transaction commit and its content file unlink: a concurrent
|
||||||
|
|||||||
Reference in New Issue
Block a user