From a6f5ba24412ce894ce3233bc1c83ce65c755900a Mon Sep 17 00:00:00 2001 From: davidby-influx Date: Tue, 21 Jul 2026 12:39:11 -0700 Subject: [PATCH] fix: permit small TSI series caches to grow --- tsdb/index/tsi1/cache.go | 16 +++++++++++++++- tsdb/index/tsi1/cache_test.go | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/tsdb/index/tsi1/cache.go b/tsdb/index/tsi1/cache.go index f39da81e92e..786e079ed61 100644 --- a/tsdb/index/tsi1/cache.go +++ b/tsdb/index/tsi1/cache.go @@ -582,7 +582,21 @@ func decideResize(hits, misses, lastHits, lastMisses, capacity, maxCapacity, min if gets == 0 { return capacity, gets, rate, false } - if gets < minSamples { + // Clamp the sample floor to capacity: the grow path fires once per + // `capacity` forced evictions, and each forced eviction follows a Get miss, + // so a thrashing cache (low hit rate — precisely the workload growth exists + // to fix) produces only ~capacity Gets per window. Demanding more than + // capacity samples would therefore gate growth off entirely for any cache + // configured smaller than minSamples, which cannot physically clear the + // floor. Clamping lets a sub-minSamples cache grow on the evidence one full + // window provides, while leaving minSamples in force for caches large enough + // to supply it. minSamples may be 0, and capacity is always >= 1 here, so the + // clamp never lowers the floor below what the gets == 0 check already gated. + effMinSamples := minSamples + if effMinSamples > capacity { + effMinSamples = capacity + } + if gets < effMinSamples { return capacity, gets, rate, false } diff --git a/tsdb/index/tsi1/cache_test.go b/tsdb/index/tsi1/cache_test.go index ed72281e312..ab597331bec 100644 --- a/tsdb/index/tsi1/cache_test.go +++ b/tsdb/index/tsi1/cache_test.go @@ -443,6 +443,26 @@ func TestDecideResize_PolicyTable(t *testing.T) { minSamples: 100, target: target, wantNewCap: initialCap * 2, wantGrow: true, }, + { + // Regression: a cache configured smaller than minSamples still grows. + // Its window is only ~capacity Gets long under thrash, so the raw + // minSamples floor could never be cleared; the floor is clamped to + // capacity. Here capacity 50 < minSamples 100, gets == capacity, rate + // 0 (< target), so growth must fire despite gets < minSamples. + name: "sub-minSamples cache grows on a full-window thrash", + hits: 0, misses: 50, capacity: 50, maxCap: maxCap, + minSamples: 100, target: target, + wantNewCap: 100, wantGrow: true, + }, + { + // The clamp lowers the floor to capacity, not to zero: a sub-minSamples + // cache with fewer than capacity Gets still has too little evidence and + // must not grow. capacity 50, gets 30 < 50 → gated. + name: "sub-minSamples cache below its clamped floor does not grow", + hits: 0, misses: 30, capacity: 50, maxCap: maxCap, + minSamples: 100, target: target, + wantNewCap: 50, wantGrow: false, + }, } for _, tt := range tests {