From ea30694dc99873a87344bd412afc062b2dd8543c Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Tue, 3 Nov 2026 00:19:35 +0000 Subject: [PATCH] fix(cache): enforce entry cap below ten in memory cache The count-based eviction removed a tenth of the cap. For a cap under ten, that was zero entries, so the cache grew without limit. The cache now evicts at least one entry when it is full. --- cache/memory/memory.go | 3 ++- cache/memory/small_cap_test.go | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 cache/memory/small_cap_test.go diff --git a/cache/memory/memory.go b/cache/memory/memory.go index c5e36c3..38600ce 100644 --- a/cache/memory/memory.go +++ b/cache/memory/memory.go @@ -158,7 +158,8 @@ func (c *Cache) Set(key string, value []byte, ttl time.Duration) { c.evictToFreeMemory(memoryToFree) } else if atomic.LoadInt64(&c.entryCount) >= c.maxCacheSize { // Fall back to count-based eviction for backward compatibility - c.evictOldest(int(c.maxCacheSize / 10)) // Evict 10% of entries + // 10% of entries, at least one: max/10 is 0 for caps below 10. + c.evictOldest(int(max(1, c.maxCacheSize/10))) } // A non-positive TTL is clamped to defaultTTL so this backend expires diff --git a/cache/memory/small_cap_test.go b/cache/memory/small_cap_test.go new file mode 100644 index 0000000..1a8f210 --- /dev/null +++ b/cache/memory/small_cap_test.go @@ -0,0 +1,23 @@ +package libpack_cache_memory + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +// Pins theorems/Theorems/MemCacheCap.lean: every cap >= 1 bounds the entry count. +func TestSet_SmallEntryCapIsEnforced(t *testing.T) { + for _, maxEntries := range []int64{1, 2, 5, 9, 10, 25} { + t.Run(fmt.Sprintf("cap_%d", maxEntries), func(t *testing.T) { + c := NewWithSize(time.Minute, DefaultMaxMemorySize, maxEntries) + defer c.Shutdown() + for i := 0; i < 100; i++ { + c.Set(fmt.Sprintf("k%d", i), []byte("v"), time.Minute) + assert.LessOrEqual(t, c.CountQueries(), maxEntries) + } + }) + } +}