R57: FIFO eviction; keep fresh replay JTIs in cache

This commit is contained in:
2026-08-13 15:55:20 +01:00
parent 49d1942de9
commit ed5f528c60
2 changed files with 76 additions and 7 deletions
+53
View File
@@ -0,0 +1,53 @@
package traefikoidc
import (
"fmt"
"testing"
"time"
)
// TestShardedCacheFIFOEvictionRetainsNewest guards against the pre-fix eviction
// policy in evictFromShardLocked, which (a) over-evicted by +10 per Set at
// capacity (each shard sat ~9 slots under its cap) and (b) deleted entries in
// random Go map-iteration order, so a just-written, still-valid-TTL entry
// (e.g. a fresh replay-protection JTI) could be dropped immediately —
// truncating the replay window.
func TestShardedCacheFIFOEvictionRetainsNewest(t *testing.T) {
cache := NewShardedCache(4, 400) // maxPerShard = 100
// Collect 100 filler keys + 1 trigger key that all hash into ONE shard,
// giving deterministic control over capacity pressure.
var shard *cacheShard
fillers := make([]string, 0, 100)
var trigger string
for i := 0; len(fillers) < 100 || trigger == ""; i++ {
k := fmt.Sprintf("key-%d", i)
s := cache.getShard(k)
if shard == nil {
shard = s
continue
}
if s == shard {
if len(fillers) < 100 {
fillers = append(fillers, k)
} else if trigger == "" {
trigger = k
}
}
}
for _, k := range fillers {
cache.Set(k, true, time.Minute)
}
// This write hits the shard at capacity: it must evict the OLDEST and
// place the newcomer, keeping the shard full.
cache.Set(trigger, true, time.Minute)
if !cache.Exists(trigger) {
t.Fatalf("capacity-triggering write evicted the just-written newest entry (random over-eviction)")
}
if n := cache.Size(); n != 100 {
t.Fatalf("shard size = %d, want 100 (old code over-evicted below capacity)", n)
}
}