Skip to content

Commit

Permalink
Exclude expired keys in data retrieval methods
Browse files Browse the repository at this point in the history
  • Loading branch information
hongkuancn committed Aug 24, 2024
1 parent 1e6fd11 commit d22fb9e
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 38 deletions.
30 changes: 20 additions & 10 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand All @@ -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
}

Expand Down
36 changes: 8 additions & 28 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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())
}

Expand All @@ -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 {
Expand Down

0 comments on commit d22fb9e

Please sign in to comment.