From c193f8a44de279130a598033f0ebcec9fac05897 Mon Sep 17 00:00:00 2001 From: Christoph Petrausch Date: Wed, 31 Aug 2016 08:35:59 +0200 Subject: [PATCH] Added function GetNotExpiredItems to get all not expired items in a threadsafe manner --- cache.go | 16 ++++++++++++++++ cache_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/cache.go b/cache.go index 3562543..19146c7 100644 --- a/cache.go +++ b/cache.go @@ -1009,6 +1009,22 @@ func (c *cache) Items() map[string]Item { return c.items } +// Returns all not expired items in the cache. This method is save to use with +// an active janitor since it copies the underlying map. Therefore method is +// costly in terms of time and memory. You can read while using this method but +// not write. Use with caution. +func (c *cache) GetNotExpiredItems() map[string]Item { + retMap := make(map[string]Item, c.ItemCount()) + c.mu.RLock() + for key, item := range c.items{ + if !item.Expired() { + retMap[key] = item + } + } + c.mu.RUnlock() + return retMap +} + // Returns the number of items in the cache. This may include items that have // expired, but have not yet been cleaned up. Equivalent to len(c.Items()). func (c *cache) ItemCount() int { diff --git a/cache_test.go b/cache_test.go index 6e81693..a2e7981 100644 --- a/cache_test.go +++ b/cache_test.go @@ -8,6 +8,7 @@ import ( "sync" "testing" "time" + "fmt" ) type TestStruct struct { @@ -1247,6 +1248,29 @@ func TestOnEvicted(t *testing.T) { } } +func TestCacheGetAllNotExpiredItems(t *testing.T) { + tc := New(time.Minute*1, 0) + tc.Set("a", "a", DefaultExpiration) + tc.Set("b", "b", DefaultExpiration) + tc.Set("c", "c", time.Millisecond*1) + time.Sleep(time.Millisecond*2) + allNotExpiredItems := tc.GetNotExpiredItems() + if len(allNotExpiredItems) != 2 { + t.Error("There are more or less items in the result than the two unexpired.") + } + for _, key := range []string{"a", "b"} { + if _, ok := tc.Get(key); !ok{ + t.Error("Could not find unexpired item %s", key) + } + } + if _, ok := tc.Get("c"); ok { + t.Error("Found expired item c.") + } + if &allNotExpiredItems == &tc.cache.items { + t.Error("Returned map is equal to internal map.") + } +} + func TestCacheSerialization(t *testing.T) { tc := New(DefaultExpiration, 0) testFillAndSerialize(t, tc) @@ -1676,3 +1700,18 @@ func BenchmarkDeleteExpiredLoop(b *testing.B) { tc.DeleteExpired() } } + +func BenchmarkGetAllNotExpiredItems(b *testing.B) { + for i:= 0; i < 20; i++ { + b.Run(fmt.Sprintf("BenchmarkGetAllNotExpiredItemsWith %d000 Items", i), func(b *testing.B){ + tc := New(20*time.Minute, 0) + for j:= 0; j < i*1000; j++{ + tc.Set(strconv.Itoa(i), "bar", DefaultExpiration) + } + b.ResetTimer() + for j:= 0; j < b.N; j++{ + tc.GetNotExpiredItems() + } + }) + } +}