From d22fb9e157281004f75b1861c1ad18d67a11ba22 Mon Sep 17 00:00:00 2001 From: Hongkuan Wang Date: Sat, 24 Aug 2024 16:53:59 +0800 Subject: [PATCH] Exclude expired keys in data retrieval methods --- cache.go | 30 ++++++++++++++++++++---------- cache_test.go | 36 ++++++++---------------------------- 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/cache.go b/cache.go index 5168762..3f2bd2d 100644 --- a/cache.go +++ b/cache.go @@ -339,8 +339,8 @@ func (c *Cache[K, V]) Has(key K) bool { c.items.mu.RLock() defer c.items.mu.RUnlock() - _, ok := c.items.values[key] - return ok + elem, ok := c.items.values[key] + return ok && !elem.Value.(*Item[K, V]).isExpiredUnsafe() } // GetOrSet retrieves an item from the cache by the provided key. @@ -440,22 +440,31 @@ func (c *Cache[K, V]) Touch(key K) { c.items.mu.Unlock() } -// Len returns the total number of items in the cache. +// Len returns the number of unexpired items in the cache. func (c *Cache[K, V]) Len() int { c.items.mu.RLock() defer c.items.mu.RUnlock() - return len(c.items.values) + size := 0 + for _, elem := range c.items.values { + if !elem.Value.(*Item[K, V]).isExpiredUnsafe() { + size++ + } + } + + return size } -// Keys returns all keys currently present in the cache. +// Keys returns all unexpired keys in the cache. func (c *Cache[K, V]) Keys() []K { c.items.mu.RLock() defer c.items.mu.RUnlock() - res := make([]K, 0, len(c.items.values)) - for k := range c.items.values { - res = append(res, k) + res := make([]K, 0) + for k, elem := range c.items.values { + if !elem.Value.(*Item[K, V]).isExpiredUnsafe() { + res = append(res, k) + } } return res @@ -478,7 +487,7 @@ func (c *Cache[K, V]) Items() map[K]*Item[K, V] { return items } -// Range calls fn for each item present in the cache. If fn returns false, +// Range calls fn for each unexpired item in the cache. If fn returns false, // Range stops the iteration. func (c *Cache[K, V]) Range(fn func(item *Item[K, V]) bool) { c.items.mu.RLock() @@ -491,9 +500,10 @@ func (c *Cache[K, V]) Range(fn func(item *Item[K, V]) bool) { for item := c.items.lru.Front(); item != c.items.lru.Back().Next(); item = item.Next() { i := item.Value.(*Item[K, V]) + expired := i.isExpiredUnsafe() c.items.mu.RUnlock() - if !fn(i) { + if !expired && !fn(i) { return } diff --git a/cache_test.go b/cache_test.go index 3b250a9..ac7db42 100644 --- a/cache_test.go +++ b/cache_test.go @@ -652,35 +652,12 @@ func Test_Cache_Delete(t *testing.T) { } func Test_Cache_Has(t *testing.T) { - cc := map[string]struct { - keys []string - searchKey string - has bool - }{ - "Empty cache": { - keys: []string{}, - searchKey: "key1", - has: false, - }, - "Key exists": { - keys: []string{"key1", "key2", "key3"}, - searchKey: "key2", - has: true, - }, - "Key doesn't exist": { - keys: []string{"key1", "key2", "key3"}, - searchKey: "key4", - has: false, - }, - } + cache := prepCache(time.Hour, "1") + addToCache(cache, time.Nanosecond, "2") - for name, tc := range cc { - t.Run(name, func(t *testing.T) { - c := prepCache(NoTTL, tc.keys...) - has := c.Has(tc.searchKey) - assert.Equal(t, tc.has, has) - }) - } + assert.True(t, cache.Has("1")) + assert.False(t, cache.Has("2")) + assert.False(t, cache.Has("3")) } func Test_Cache_GetOrSet(t *testing.T) { @@ -827,11 +804,13 @@ func Test_Cache_Touch(t *testing.T) { func Test_Cache_Len(t *testing.T) { cache := prepCache(time.Hour, "1", "2") + addToCache(cache, time.Nanosecond, "3") assert.Equal(t, 2, cache.Len()) } func Test_Cache_Keys(t *testing.T) { cache := prepCache(time.Hour, "1", "2", "3") + addToCache(cache, time.Nanosecond, "4") assert.ElementsMatch(t, []string{"1", "2", "3"}, cache.Keys()) } @@ -851,6 +830,7 @@ func Test_Cache_Items(t *testing.T) { func Test_Cache_Range(t *testing.T) { c := prepCache(DefaultTTL, "1", "2", "3", "4", "5") + addToCache(c, time.Nanosecond, "6") var results []string c.Range(func(item *Item[string, string]) bool {