Skip to content

Commit 923d329

Browse files
authored
Add RangeBackwards method
1 parent d22fb9e commit 923d329

File tree

2 files changed

+52
-4
lines changed

2 files changed

+52
-4
lines changed

cache.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,32 @@ func (c *Cache[K, V]) Range(fn func(item *Item[K, V]) bool) {
513513
}
514514
}
515515

516+
// RangeBackwards calls fn for each unexpired item in the cache in reverse order.
517+
// If fn returns false, RangeBackwards stops the iteration.
518+
func (c *Cache[K, V]) RangeBackwards(fn func(item *Item[K, V]) bool) {
519+
c.items.mu.RLock()
520+
521+
// Check if cache is empty
522+
if c.items.lru.Len() == 0 {
523+
c.items.mu.RUnlock()
524+
return
525+
}
526+
527+
for item := c.items.lru.Back(); item != c.items.lru.Front().Prev(); item = item.Prev() {
528+
i := item.Value.(*Item[K, V])
529+
expired := i.isExpiredUnsafe()
530+
c.items.mu.RUnlock()
531+
532+
if !expired && !fn(i) {
533+
return
534+
}
535+
536+
if item.Prev() != nil {
537+
c.items.mu.RLock()
538+
}
539+
}
540+
}
541+
516542
// Metrics returns the metrics of the cache.
517543
func (c *Cache[K, V]) Metrics() Metrics {
518544
c.metricsMu.RLock()

cache_test.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -329,18 +329,18 @@ func Test_Cache_set(t *testing.T) {
329329
// recreate situation when expired item gets updated
330330
// and not auto-cleaned up yet.
331331
c := New[string, struct{}](
332-
WithDisableTouchOnHit[string,struct{}](),
332+
WithDisableTouchOnHit[string, struct{}](),
333333
)
334334

335335
// insert an item and let it expire
336336
c.Set("test", struct{}{}, 1)
337-
time.Sleep(50*time.Millisecond)
337+
time.Sleep(50 * time.Millisecond)
338338

339339
// update the expired item
340340
updatedItem := c.Set("test", struct{}{}, 100*time.Millisecond)
341341

342342
// eviction should not happen as we prolonged element
343-
cl := c.OnEviction(func(_ context.Context, _ EvictionReason, item *Item[string, struct{}]){
343+
cl := c.OnEviction(func(_ context.Context, _ EvictionReason, item *Item[string, struct{}]) {
344344
t.Errorf("eviction happened even though item has not expired yet: key=%s, evicted item expiresAt=%s, updated item expiresAt=%s, now=%s",
345345
item.Key(),
346346
item.ExpiresAt().String(),
@@ -351,7 +351,7 @@ func Test_Cache_set(t *testing.T) {
351351
// and update expired before its removal
352352
go c.Start()
353353

354-
time.Sleep(90*time.Millisecond)
354+
time.Sleep(90 * time.Millisecond)
355355
cl()
356356
c.Stop()
357357
}
@@ -848,6 +848,28 @@ func Test_Cache_Range(t *testing.T) {
848848
})
849849
}
850850

851+
func Test_Cache_RangeBackwards(t *testing.T) {
852+
c := prepCache(DefaultTTL)
853+
addToCache(c, time.Nanosecond, "1")
854+
addToCache(c, time.Hour, "2", "3", "4", "5")
855+
856+
var results []string
857+
858+
c.RangeBackwards(func(item *Item[string, string]) bool {
859+
results = append(results, item.Key())
860+
return item.Key() != "4"
861+
})
862+
863+
assert.Equal(t, []string{"2", "3", "4"}, results)
864+
865+
emptyCache := New[string, string]()
866+
assert.NotPanics(t, func() {
867+
emptyCache.RangeBackwards(func(item *Item[string, string]) bool {
868+
return false
869+
})
870+
})
871+
}
872+
851873
func Test_Cache_Metrics(t *testing.T) {
852874
cache := Cache[string, string]{
853875
metrics: Metrics{Evictions: 10},

0 commit comments

Comments
 (0)