Serve a cache hit with no database read or write (closes #309)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
A hit opens the variant file and nothing else: its content type comes from the file's first bytes, and a file in no output format is a miss and is made again. Its access time and hit count are handed, without waiting, to the goroutine that wrote late counts, which now writes every count and the access times once a second; the access times wait in a channel of 10,000 that drops the rest. checkNegativeCache leaves expired rows to CleanExpired. The .meta files, metaCache, Lookup and golang-lru go, with their tests; tests that read counts after Get wait for the background write. make bench runs a parallel cache hit benchmark, outside make check. Model: opus-5-5
This commit is contained in:
@@ -148,43 +148,6 @@ func TestStats_LogsFailedCountQueries(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestIncrementStats_LogsFailedUpdates verifies that a failed hit or miss
|
||||
// count update and a failed upstream fetch count update are each logged at
|
||||
// warn.
|
||||
func TestIncrementStats_LogsFailedUpdates(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db := setupStatsTestDB(t)
|
||||
|
||||
var logBuf bytes.Buffer
|
||||
|
||||
cache, err := NewCache(db, CacheConfig{
|
||||
StateDir: t.TempDir(),
|
||||
NegativeTTL: 5 * time.Minute,
|
||||
Logger: slog.New(slog.NewJSONHandler(&logBuf, nil)),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = db.ExecContext(t.Context(), `DROP TABLE cache_stats`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cache.IncrementStats(t.Context(), false, 1024)
|
||||
|
||||
for _, msg := range []string{
|
||||
"failed to count cache hit or miss",
|
||||
"failed to count upstream fetch",
|
||||
} {
|
||||
want := `"level":"WARN","msg":"` + msg + `"`
|
||||
if !strings.Contains(logBuf.String(), want) {
|
||||
t.Errorf("log missing %s; got %q", want, logBuf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestStats_TotalsCountSourcesAndVariants verifies that TotalItems and
|
||||
// TotalSizeBytes cover the stored source images and processed variants.
|
||||
func TestStats_TotalsCountSourcesAndVariants(t *testing.T) {
|
||||
@@ -340,6 +303,8 @@ func TestService_Get_CountsStats(t *testing.T) {
|
||||
_ = resp.Content.Close()
|
||||
}
|
||||
|
||||
waitForPendingCountsWritten(t, svc.cache)
|
||||
|
||||
got := readCacheStatsCounters(t, svc.cache)
|
||||
if got != step.want {
|
||||
t.Fatalf("after the %s: counters = %+v, want %+v", step.name, got, step.want)
|
||||
@@ -446,6 +411,8 @@ func TestService_Get_CountsInterruptedMisses(t *testing.T) {
|
||||
_ = resp.Content.Close()
|
||||
}
|
||||
|
||||
waitForPendingCountsWritten(t, svc.cache)
|
||||
|
||||
got := readCacheStatsCounters(t, svc.cache)
|
||||
if got != tc.want {
|
||||
t.Errorf("counters = %+v, want %+v", got, tc.want)
|
||||
@@ -483,6 +450,8 @@ func TestService_Get_CountsHitAfterRequestEnds(t *testing.T) {
|
||||
|
||||
_ = resp.Content.Close()
|
||||
|
||||
waitForPendingCountsWritten(t, svc.cache)
|
||||
|
||||
want := readCacheStatsCounters(t, svc.cache)
|
||||
want.hitCount++
|
||||
|
||||
@@ -500,6 +469,8 @@ func TestService_Get_CountsHitAfterRequestEnds(t *testing.T) {
|
||||
t.Fatalf("CacheStatus = %v, want %v", resp.CacheStatus, CacheHit)
|
||||
}
|
||||
|
||||
waitForPendingCountsWritten(t, svc.cache)
|
||||
|
||||
got := readCacheStatsCounters(t, svc.cache)
|
||||
if got != want {
|
||||
t.Errorf("counters = %+v, want %+v", got, want)
|
||||
|
||||
Reference in New Issue
Block a user