cache: Short TTL negative cache for 404/410 depot objects
CI / vulncheck (pull_request) Successful in 14s
CI / check-and-test (pull_request) Successful in 41s

Stop re-fetching gone depot objects on every miss: store 404/410 in the
existing VFS cache under the same key with a short TTL (default 5m).
This commit is contained in:
2026-09-08 16:40:03 +00:00
parent 8eb31143e5
commit 036ea1ea7f
10 changed files with 550 additions and 89 deletions
+23 -13
View File
@@ -27,7 +27,7 @@ import (
func TestCaching(t *testing.T) {
td := t.TempDir()
sc, err := New("localhost:8080", "1G", "1G", td, "", "lru", "lru", 200, 5, "0", nil)
sc, err := New("localhost:8080", "1G", "1G", td, "", "lru", "lru", 200, 5, "0", nil, "")
if err != nil {
t.Fatalf("failed to create SteamCache: %v", err)
}
@@ -132,7 +132,7 @@ func TestCaching(t *testing.T) {
}
func TestCacheMissAndHit(t *testing.T) {
sc, err := New("localhost:8080", "1MB", "1G", t.TempDir(), "", "lru", "lru", 200, 5, "0", nil)
sc, err := New("localhost:8080", "1MB", "1G", t.TempDir(), "", "lru", "lru", 200, 5, "0", nil, "")
if err != nil {
t.Fatalf("failed to create SteamCache: %v", err)
}
@@ -375,7 +375,7 @@ func TestServiceManagerExpandability(t *testing.T) {
// Removed hash calculation tests since we switched to lightweight validation
func TestSteamKeySharding(t *testing.T) {
sc, err := New("localhost:8080", "1MB", "1G", t.TempDir(), "", "lru", "lru", 200, 5, "0", nil)
sc, err := New("localhost:8080", "1MB", "1G", t.TempDir(), "", "lru", "lru", 200, 5, "0", nil, "")
if err != nil {
t.Fatalf("failed to create SteamCache: %v", err)
}
@@ -482,7 +482,7 @@ func TestErrorTypes(t *testing.T) {
// TestMetrics tests the metrics functionality
func TestMetrics(t *testing.T) {
td := t.TempDir()
sc, err := New("localhost:8080", "1G", "1G", td, "", "lru", "lru", 200, 5, "0", nil)
sc, err := New("localhost:8080", "1G", "1G", td, "", "lru", "lru", 200, 5, "0", nil, "")
if err != nil {
t.Fatalf("failed to create SteamCache: %v", err)
}
@@ -501,6 +501,7 @@ func TestMetrics(t *testing.T) {
sc.metrics.IncrementTotalRequests()
sc.metrics.IncrementCacheHits()
sc.metrics.IncrementCacheMisses()
sc.metrics.IncrementNegativeCacheHits()
sc.metrics.AddBytesServed(1024)
sc.metrics.IncrementServiceRequests("steam")
@@ -514,6 +515,9 @@ func TestMetrics(t *testing.T) {
if stats.CacheMisses != 1 {
t.Error("Cache misses should be 1")
}
if stats.NegativeCacheHits != 1 {
t.Error("Negative cache hits should be 1")
}
if stats.TotalBytesServed != 1024 {
t.Error("Total bytes served should be 1024")
}
@@ -541,6 +545,9 @@ func TestMetrics(t *testing.T) {
if stats.CacheHits != 0 {
t.Error("After reset, cache hits should be 0")
}
if stats.NegativeCacheHits != 0 {
t.Error("After reset, negative cache hits should be 0")
}
// Phase 3: exercise newly exported WriteText (cheap coverage for promotion)
rec := httptest.NewRecorder()
@@ -551,6 +558,9 @@ func TestMetrics(t *testing.T) {
if !bytes.Contains(rec.Body.Bytes(), []byte("total_requests")) {
t.Error("WriteText output missing expected key")
}
if !bytes.Contains(rec.Body.Bytes(), []byte("negative_cache_hits")) {
t.Error("WriteText output missing negative_cache_hits")
}
}
// Removed old TestKeyGeneration - replaced with TestURLHashing that uses SHA256
@@ -570,7 +580,7 @@ func newTestCacheWithFakeUpstream(t *testing.T, h http.HandlerFunc, mem, disk st
s := httptest.NewServer(h)
t.Cleanup(s.Close)
d := t.TempDir()
sc, err := New("127.0.0.1:0", mem, disk, d, s.URL, "lru", "lru", 200, 10, "0", nil)
sc, err := New("127.0.0.1:0", mem, disk, d, s.URL, "lru", "lru", 200, 10, "0", nil, "")
if err != nil {
t.Fatalf("failed to create SteamCache: %v", err)
}
@@ -732,7 +742,7 @@ func TestErrorMetrics(t *testing.T) {
// Cover 503 capacity path + accounting skew: force Acquire err via canceled ctx.
// Asserts Errors+RateLimited inc, Total unchanged (per documented design in code comment).
tdCap := t.TempDir()
scCap, err := New("127.0.0.1:0", "1MB", "0", tdCap, "", "lru", "lru", 200, 5, "0", nil)
scCap, err := New("127.0.0.1:0", "1MB", "0", tdCap, "", "lru", "lru", 200, 5, "0", nil, "")
if err != nil {
t.Fatalf("cap sc: %v", err)
}
@@ -796,7 +806,7 @@ func TestErrorMetrics(t *testing.T) {
func TestExpandedErrorMetrics(t *testing.T) {
t.Parallel()
td := t.TempDir()
sc, err := New("localhost:0", "1MB", "0", td, "", "lru", "lru", 10, 5, "0", nil)
sc, err := New("localhost:0", "1MB", "0", td, "", "lru", "lru", 10, 5, "0", nil, "")
if err != nil {
t.Fatalf("create: %v", err)
}
@@ -886,7 +896,7 @@ func TestNewInvalidSizes(t *testing.T) {
}
for _, c := range cases {
t.Run(c.mem+"_"+c.disk, func(t *testing.T) {
sc, err := New("127.0.0.1:0", c.mem, c.disk, t.TempDir(), "", "lru", "lru", 10, 5, c.maxobj, nil)
sc, err := New("127.0.0.1:0", c.mem, c.disk, t.TempDir(), "", "lru", "lru", 10, 5, c.maxobj, nil, "")
if err == nil {
t.Fatal("expected error for bad size, got nil")
}
@@ -907,7 +917,7 @@ func TestNewRunShutdownHygiene(t *testing.T) {
t.Skip("skips Run hygiene in -short per existing pattern")
}
d := t.TempDir()
sc, err := New("127.0.0.1:0", "1MB", "0", d, "", "lru", "lru", 10, 5, "0", nil)
sc, err := New("127.0.0.1:0", "1MB", "0", d, "", "lru", "lru", 10, 5, "0", nil, "")
if err != nil {
t.Fatalf("new: %v", err)
}
@@ -1048,7 +1058,7 @@ func TestDiskOnlyDelayedAttach(t *testing.T) {
}
// mem=0, disk>0 -> pure disk delayed path (go func)
sc, err := New("localhost:0", "0", "10MB", diskPath, "", "lru", "lru", 10, 1, "0", nil)
sc, err := New("localhost:0", "0", "10MB", diskPath, "", "lru", "lru", 10, 1, "0", nil, "")
if err != nil {
t.Fatalf("New disk-only: %v", err)
}
@@ -1109,7 +1119,7 @@ func TestDiskOnlyDelayedAttach(t *testing.T) {
// TestDiskTierSignalMemoryOnly covers memory-only mode: DiskTierReady=1 (N/A, not
// waiting on disk attach) and heartbeat header X-SteamCache-Disk-Tier: disabled.
func TestDiskTierSignalMemoryOnly(t *testing.T) {
sc, err := New("127.0.0.1:0", "1MB", "0", t.TempDir(), "", "lru", "lru", 10, 5, "0", nil)
sc, err := New("127.0.0.1:0", "1MB", "0", t.TempDir(), "", "lru", "lru", 10, 5, "0", nil, "")
if err != nil {
t.Fatalf("New memory-only: %v", err)
}
@@ -1142,7 +1152,7 @@ func TestDiskTierSignalMixedPendingReady(t *testing.T) {
if err := os.MkdirAll(diskPath, 0755); err != nil {
t.Fatal(err)
}
sc, err := New("127.0.0.1:0", "1MB", "10MB", diskPath, "", "lru", "lru", 10, 1, "0", nil)
sc, err := New("127.0.0.1:0", "1MB", "10MB", diskPath, "", "lru", "lru", 10, 1, "0", nil, "")
if err != nil {
t.Fatalf("New mixed: %v", err)
}
@@ -1293,7 +1303,7 @@ func TestHostAllowedForDirectFetch(t *testing.T) {
func TestDirectFetchRejectsNonSteamHost(t *testing.T) {
td := t.TempDir()
sc, err := New("127.0.0.1:0", "1MB", "0", td, "", "lru", "lru", 200, 5, "0", nil)
sc, err := New("127.0.0.1:0", "1MB", "0", td, "", "lru", "lru", 200, 5, "0", nil, "")
if err != nil {
t.Fatalf("New: %v", err)
}