@@ -134,8 +134,8 @@ type ExpandedPostingsCache interface {
134134type blocksPostingsForMatchersCache struct {
135135 userId string
136136
137- headCache * fifoCache [[]storage.SeriesRef ]
138- blocksCache * fifoCache [[]storage.SeriesRef ]
137+ headCache * lruCache [[]storage.SeriesRef ]
138+ blocksCache * lruCache [[]storage.SeriesRef ]
139139 postingsForMatchersFunc func (ctx context.Context , ix tsdb.IndexReader , ms ... * labels.Matcher ) (index.Postings , error )
140140 timeNow func () time.Time
141141
@@ -158,8 +158,8 @@ func newBlocksPostingsForMatchersCache(userId string, cfg TSDBPostingsCacheConfi
158158 }
159159
160160 return & blocksPostingsForMatchersCache {
161- headCache : newFifoCache [[]storage.SeriesRef ](cfg .Head , "head" , metrics , cfg .timeNow ),
162- blocksCache : newFifoCache [[]storage.SeriesRef ](cfg .Blocks , "block" , metrics , cfg .timeNow ),
161+ headCache : newLruCache [[]storage.SeriesRef ](cfg .Head , "head" , metrics , cfg .timeNow ),
162+ blocksCache : newLruCache [[]storage.SeriesRef ](cfg .Blocks , "block" , metrics , cfg .timeNow ),
163163 postingsForMatchersFunc : cfg .PostingsForMatchers ,
164164 timeNow : cfg .timeNow ,
165165 metrics : metrics ,
@@ -352,7 +352,7 @@ func (s *seedByHash) incrementSeed(userId string, v string) {
352352 s .seedByHash [i ]++
353353}
354354
355- type fifoCache [V any ] struct {
355+ type lruCache [V any ] struct {
356356 cfg PostingsCacheConfig
357357 cachedValues * sync.Map
358358 timeNow func () time.Time
@@ -365,8 +365,8 @@ type fifoCache[V any] struct {
365365 cachedBytes int64
366366}
367367
368- func newFifoCache [V any ](cfg PostingsCacheConfig , name string , metrics * ExpandedPostingsCacheMetrics , timeNow func () time.Time ) * fifoCache [V ] {
369- return & fifoCache [V ]{
368+ func newLruCache [V any ](cfg PostingsCacheConfig , name string , metrics * ExpandedPostingsCacheMetrics , timeNow func () time.Time ) * lruCache [V ] {
369+ return & lruCache [V ]{
370370 cachedValues : new (sync.Map ),
371371 cached : list .New (),
372372 cfg : cfg ,
@@ -376,15 +376,15 @@ func newFifoCache[V any](cfg PostingsCacheConfig, name string, metrics *Expanded
376376 }
377377}
378378
379- func (c * fifoCache [V ]) clear () {
379+ func (c * lruCache [V ]) clear () {
380380 c .cachedMtx .Lock ()
381381 defer c .cachedMtx .Unlock ()
382382 c .cached = list .New ()
383383 c .cachedBytes = 0
384384 c .cachedValues = new (sync.Map )
385385}
386386
387- func (c * fifoCache [V ]) expire () {
387+ func (c * lruCache [V ]) expire () {
388388 if c .cfg .Ttl <= 0 {
389389 return
390390 }
@@ -402,13 +402,13 @@ func (c *fifoCache[V]) expire() {
402402 }
403403}
404404
405- func (c * fifoCache [V ]) size () int {
405+ func (c * lruCache [V ]) size () int {
406406 c .cachedMtx .RLock ()
407407 defer c .cachedMtx .RUnlock ()
408408 return c .cached .Len ()
409409}
410410
411- func (c * fifoCache [V ]) getPromiseForKey (k string , fetch func () (V , int64 , error )) (* cacheEntryPromise [V ], bool ) {
411+ func (c * lruCache [V ]) getPromiseForKey (k string , fetch func () (V , int64 , error )) (* cacheEntryPromise [V ], bool ) {
412412 r := & cacheEntryPromise [V ]{
413413 done : make (chan struct {}),
414414 }
@@ -434,6 +434,13 @@ func (c *fifoCache[V]) getPromiseForKey(k string, fetch func() (V, int64, error)
434434 // If the promise is already in the cache, lets wait it to fetch the data.
435435 <- loaded .(* cacheEntryPromise [V ]).done
436436
437+ // LRU: move to back on access
438+ if elem := loaded .(* cacheEntryPromise [V ]).elem ; elem != nil {
439+ c .cachedMtx .Lock ()
440+ c .cached .MoveToBack (elem )
441+ c .cachedMtx .Unlock ()
442+ }
443+
437444 // If is cached but is expired, lets try to replace the cache value.
438445 if loaded .(* cacheEntryPromise [V ]).isExpired (c .cfg .Ttl , c .timeNow ()) && c .cachedValues .CompareAndSwap (k , loaded , r ) {
439446 c .metrics .CacheMiss .WithLabelValues (c .name , "expired" ).Inc ()
@@ -449,12 +456,12 @@ func (c *fifoCache[V]) getPromiseForKey(k string, fetch func() (V, int64, error)
449456 return loaded .(* cacheEntryPromise [V ]), ok
450457}
451458
452- func (c * fifoCache [V ]) contains (k string ) bool {
459+ func (c * lruCache [V ]) contains (k string ) bool {
453460 _ , ok := c .cachedValues .Load (k )
454461 return ok
455462}
456463
457- func (c * fifoCache [V ]) shouldEvictHead () (string , bool ) {
464+ func (c * lruCache [V ]) shouldEvictHead () (string , bool ) {
458465 h := c .cached .Front ()
459466 if h == nil {
460467 return "" , false
@@ -475,7 +482,7 @@ func (c *fifoCache[V]) shouldEvictHead() (string, bool) {
475482 return "" , false
476483}
477484
478- func (c * fifoCache [V ]) evictHead () {
485+ func (c * lruCache [V ]) evictHead () {
479486 front := c .cached .Front ()
480487 c .cached .Remove (front )
481488 oldestKey := front .Value .(string )
@@ -484,18 +491,22 @@ func (c *fifoCache[V]) evictHead() {
484491 }
485492}
486493
487- func (c * fifoCache [V ]) created (key string , sizeBytes int64 ) {
494+ func (c * lruCache [V ]) created (key string , sizeBytes int64 ) {
488495 if c .cfg .Ttl <= 0 {
489496 c .cachedValues .Delete (key )
490497 return
491498 }
492499 c .cachedMtx .Lock ()
493500 defer c .cachedMtx .Unlock ()
494- c .cached .PushBack (key )
501+ elem := c .cached .PushBack (key )
502+ // Store the element reference in the promise for O(1) LRU access
503+ if p , ok := c .cachedValues .Load (key ); ok {
504+ p .(* cacheEntryPromise [V ]).elem = elem
505+ }
495506 c .cachedBytes += sizeBytes
496507}
497508
498- func (c * fifoCache [V ]) updateSize (oldSize , newSizeBytes int64 ) {
509+ func (c * lruCache [V ]) updateSize (oldSize , newSizeBytes int64 ) {
499510 if oldSize == newSizeBytes {
500511 return
501512 }
@@ -508,6 +519,7 @@ func (c *fifoCache[V]) updateSize(oldSize, newSizeBytes int64) {
508519type cacheEntryPromise [V any ] struct {
509520 ts time.Time
510521 sizeBytes int64
522+ elem * list.Element
511523
512524 done chan struct {}
513525 v V
0 commit comments