Test that reconciliation drops a variant's row only when its file is missing
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
reconcileVariantRows is given two tracked variants. The first one's file is a symbolic link to itself, so reading its size fails with an error other than a missing file. The test wants its row kept, with a warning naming its key and the error. The second one has no file, and the test wants its row dropped with the info line for a missing file. The test fails on next, which drops both rows and logs both files as missing. The fix follows in the next commit. Model: opus-5-5
This commit is contained in:
@@ -699,6 +699,92 @@ func TestStartEvictionReconcilesAccountingWithDisk(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// logLineNaming returns the first line of logs, written by slog's JSON
|
||||
// handler, that is at level and names the variant key, or "" if none is.
|
||||
func logLineNaming(logs, level string, key imagerequest.VariantKey) string {
|
||||
for line := range strings.Lines(logs) {
|
||||
if strings.Contains(line, `"level":"`+level+`"`) &&
|
||||
strings.Contains(line, `"cache_key":"`+string(key)+`"`) {
|
||||
return line
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// TestReconcileVariantRowsDropsOnlyRowsOfMissingFiles gives
|
||||
// reconcileVariantRows two tracked variants. The first one's file is a
|
||||
// symbolic link to itself, so its size cannot be read. Its row must stay,
|
||||
// with a warning naming its key and the error. The second one has no file.
|
||||
// Its row must go, with the info line for a missing file.
|
||||
func TestReconcileVariantRowsDropsOnlyRowsOfMissingFiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cache, _ := newEvictionTestCache(t, 1<<30)
|
||||
storeEvictionTestVariant(t, cache, testVariantKeyOne, []byte("variant one"))
|
||||
storeEvictionTestVariant(t, cache, testVariantKeyTwo, []byte("variant two"))
|
||||
|
||||
unreadablePath := cache.variants.keyToPath(string(testVariantKeyOne))
|
||||
|
||||
err := os.Remove(unreadablePath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to remove the first variant's file: %v", err)
|
||||
}
|
||||
|
||||
err = os.Symlink(unreadablePath, unreadablePath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to link the first variant's path to itself: %v", err)
|
||||
}
|
||||
|
||||
_, statErr := os.Stat(unreadablePath)
|
||||
if statErr == nil || errors.Is(statErr, fs.ErrNotExist) {
|
||||
t.Fatalf("os.Stat() of the first variant: error = %v, "+
|
||||
"want an error other than a missing file", statErr)
|
||||
}
|
||||
|
||||
err = os.Remove(cache.variants.keyToPath(string(testVariantKeyTwo)))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to remove the second variant's file: %v", err)
|
||||
}
|
||||
|
||||
var logBuf bytes.Buffer
|
||||
|
||||
cache.log = slog.New(slog.NewJSONHandler(&logBuf, nil))
|
||||
|
||||
err = cache.reconcileVariantRows(t.Context())
|
||||
t.Logf("reconcileVariantRows() error = %v, log: %s", err, logBuf.String())
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("reconcileVariantRows() error = %v", err)
|
||||
}
|
||||
|
||||
rowQuery := `SELECT COUNT(*) FROM variant_content WHERE cache_key = ?`
|
||||
|
||||
if n := countRows(t, cache, rowQuery, string(testVariantKeyOne)); n != 1 {
|
||||
t.Errorf("the variant whose size cannot be read has %d rows, want 1", n)
|
||||
}
|
||||
|
||||
if n := countRows(t, cache, rowQuery, string(testVariantKeyTwo)); n != 0 {
|
||||
t.Errorf("the variant with no file has %d rows, want 0", n)
|
||||
}
|
||||
|
||||
logs := logBuf.String()
|
||||
|
||||
warning := logLineNaming(logs, "WARN", testVariantKeyOne)
|
||||
if !strings.Contains(warning, statErr.Error()) {
|
||||
t.Errorf("no warning names %s and the error %q", testVariantKeyOne, statErr)
|
||||
}
|
||||
|
||||
if line := logLineNaming(logs, "INFO", testVariantKeyOne); line != "" {
|
||||
t.Errorf("the variant whose size cannot be read was logged at info: %s", line)
|
||||
}
|
||||
|
||||
dropped := logLineNaming(logs, "INFO", testVariantKeyTwo)
|
||||
if !strings.Contains(dropped, "dropped accounting row for missing variant file") {
|
||||
t.Errorf("no info line says the file of %s is missing", testVariantKeyTwo)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user