Skip to content

Commit

Permalink
Add RangeBackwards method
Browse files Browse the repository at this point in the history
  • Loading branch information
k1LoW authored Aug 27, 2024
1 parent d22fb9e commit 923d329
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 4 deletions.
26 changes: 26 additions & 0 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,32 @@ func (c *Cache[K, V]) Range(fn func(item *Item[K, V]) bool) {
}
}

// RangeBackwards calls fn for each unexpired item in the cache in reverse order.
// If fn returns false, RangeBackwards stops the iteration.
func (c *Cache[K, V]) RangeBackwards(fn func(item *Item[K, V]) bool) {
c.items.mu.RLock()

// Check if cache is empty
if c.items.lru.Len() == 0 {
c.items.mu.RUnlock()
return
}

for item := c.items.lru.Back(); item != c.items.lru.Front().Prev(); item = item.Prev() {
i := item.Value.(*Item[K, V])
expired := i.isExpiredUnsafe()
c.items.mu.RUnlock()

if !expired && !fn(i) {
return
}

if item.Prev() != nil {
c.items.mu.RLock()
}
}
}

// Metrics returns the metrics of the cache.
func (c *Cache[K, V]) Metrics() Metrics {
c.metricsMu.RLock()
Expand Down
30 changes: 26 additions & 4 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,18 +329,18 @@ func Test_Cache_set(t *testing.T) {
// recreate situation when expired item gets updated
// and not auto-cleaned up yet.
c := New[string, struct{}](
WithDisableTouchOnHit[string,struct{}](),
WithDisableTouchOnHit[string, struct{}](),
)

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

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

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

time.Sleep(90*time.Millisecond)
time.Sleep(90 * time.Millisecond)
cl()
c.Stop()
}
Expand Down Expand Up @@ -848,6 +848,28 @@ func Test_Cache_Range(t *testing.T) {
})
}

func Test_Cache_RangeBackwards(t *testing.T) {
c := prepCache(DefaultTTL)
addToCache(c, time.Nanosecond, "1")
addToCache(c, time.Hour, "2", "3", "4", "5")

var results []string

c.RangeBackwards(func(item *Item[string, string]) bool {
results = append(results, item.Key())
return item.Key() != "4"
})

assert.Equal(t, []string{"2", "3", "4"}, results)

emptyCache := New[string, string]()
assert.NotPanics(t, func() {
emptyCache.RangeBackwards(func(item *Item[string, string]) bool {
return false
})
})
}

func Test_Cache_Metrics(t *testing.T) {
cache := Cache[string, string]{
metrics: Metrics{Evictions: 10},
Expand Down

0 comments on commit 923d329

Please sign in to comment.