From 482fbabc6e503c69df01d0ddb881b7cf6ea4992c Mon Sep 17 00:00:00 2001 From: Leo Antunes Date: Mon, 5 Aug 2024 18:43:22 +0200 Subject: [PATCH] feat: add reason to OnEvictCallback --- s3fifo/s3fifo.go | 15 ++++++++------- s3fifo/s3fifo_test.go | 5 +++-- sieve/sieve.go | 12 ++++++------ sieve/sieve_test.go | 5 +++-- types/types.go | 15 ++++++++++++++- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/s3fifo/s3fifo.go b/s3fifo/s3fifo.go index eaff94f..f78a54c 100644 --- a/s3fifo/s3fifo.go +++ b/s3fifo/s3fifo.go @@ -3,9 +3,10 @@ package s3fifo import ( "container/list" "context" - "github.com/scalalang2/golang-fifo/types" "sync" "time" + + "github.com/scalalang2/golang-fifo/types" ) const numberOfBuckets = 100 @@ -150,7 +151,7 @@ func (s *S3FIFO[K, V]) Remove(key K) (ok bool) { s.mu.Lock() defer s.mu.Unlock() if e, ok := s.items[key]; ok { - s.removeEntry(e) + s.removeEntry(e, types.EvictReasonRemoved) return true } @@ -209,9 +210,9 @@ func (s *S3FIFO[K, V]) Close() { s.mu.Unlock() } -func (s *S3FIFO[K, V]) removeEntry(e *entry[K, V]) { +func (s *S3FIFO[K, V]) removeEntry(e *entry[K, V], reason types.EvictReason) { if s.callback != nil { - s.callback(e.key, e.value) + s.callback(e.key, e.value, reason) } if s.ghost.contains(e.key) { @@ -256,7 +257,7 @@ func (s *S3FIFO[K, V]) deleteExpired() { } for _, e := range bucket.entries { - s.removeEntry(e) + s.removeEntry(e, types.EvictReasonExpired) } s.mu.Unlock() @@ -292,7 +293,7 @@ func (s *S3FIFO[K, V]) evictFromSmall() { s.evictFromMain() } } else { - s.removeEntry(el) + s.removeEntry(el, types.EvictReasonEvicted) s.ghost.add(key) evicted = true delete(s.items, key) @@ -314,7 +315,7 @@ func (s *S3FIFO[K, V]) evictFromMain() { s.items[key].freq -= 1 s.items[key].element = s.main.PushFront(el.key) } else { - s.removeEntry(el) + s.removeEntry(el, types.EvictReasonEvicted) evicted = true delete(s.items, key) } diff --git a/s3fifo/s3fifo_test.go b/s3fifo/s3fifo_test.go index 6e93e9e..2c0c885 100644 --- a/s3fifo/s3fifo_test.go +++ b/s3fifo/s3fifo_test.go @@ -6,6 +6,7 @@ import ( "time" "fortio.org/assert" + "github.com/scalalang2/golang-fifo/types" ) const noEvictionTTL = 0 @@ -192,7 +193,7 @@ func TestEvictionCallback(t *testing.T) { cache := New[int, int](10, noEvictionTTL) evicted := make(map[int]int) - cache.SetOnEvicted(func(key int, value int) { + cache.SetOnEvicted(func(key int, value int, _ types.EvictReason) { evicted[key] = value }) @@ -216,7 +217,7 @@ func TestEvictionCallbackWithTTL(t *testing.T) { var mu sync.Mutex cache := New[int, int](10, time.Second) evicted := make(map[int]int) - cache.SetOnEvicted(func(key int, value int) { + cache.SetOnEvicted(func(key int, value int, _ types.EvictReason) { mu.Lock() evicted[key] = value mu.Unlock() diff --git a/sieve/sieve.go b/sieve/sieve.go index e440aa8..d00a3a1 100644 --- a/sieve/sieve.go +++ b/sieve/sieve.go @@ -149,7 +149,7 @@ func (s *Sieve[K, V]) Remove(key K) (ok bool) { s.hand = s.hand.Prev() } - s.removeEntry(e) + s.removeEntry(e, types.EvictReasonRemoved) return true } @@ -193,7 +193,7 @@ func (s *Sieve[K, V]) Purge() { defer s.mu.Unlock() for _, e := range s.items { - s.removeEntry(e) + s.removeEntry(e, types.EvictReasonRemoved) } for i := range s.buckets { @@ -215,9 +215,9 @@ func (s *Sieve[K, V]) Close() { s.mu.Unlock() } -func (s *Sieve[K, V]) removeEntry(e *entry[K, V]) { +func (s *Sieve[K, V]) removeEntry(e *entry[K, V], reason types.EvictReason) { if s.callback != nil { - s.callback(e.key, e.value) + s.callback(e.key, e.value, reason) } s.ll.Remove(e.element) @@ -251,7 +251,7 @@ func (s *Sieve[K, V]) evict() { } s.hand = o.Prev() - s.removeEntry(el) + s.removeEntry(el, types.EvictReasonEvicted) } func (s *Sieve[K, V]) addToBucket(e *entry[K, V]) { @@ -287,7 +287,7 @@ func (s *Sieve[K, V]) deleteExpired() { } for _, e := range bucket.entries { - s.removeEntry(e) + s.removeEntry(e, types.EvictReasonExpired) } s.mu.Unlock() diff --git a/sieve/sieve_test.go b/sieve/sieve_test.go index 30af8bb..48ad67c 100644 --- a/sieve/sieve_test.go +++ b/sieve/sieve_test.go @@ -6,6 +6,7 @@ import ( "time" "fortio.org/assert" + "github.com/scalalang2/golang-fifo/types" ) const noEvictionTTL = 0 @@ -146,7 +147,7 @@ func TestEvictionCallback(t *testing.T) { cache := New[int, int](10, noEvictionTTL) evicted := make(map[int]int) - cache.SetOnEvicted(func(key int, value int) { + cache.SetOnEvicted(func(key int, value int, _ types.EvictReason) { evicted[key] = value }) @@ -170,7 +171,7 @@ func TestEvictionCallbackWithTTL(t *testing.T) { var mu sync.Mutex cache := New[int, int](10, time.Second) evicted := make(map[int]int) - cache.SetOnEvicted(func(key int, value int) { + cache.SetOnEvicted(func(key int, value int, _ types.EvictReason) { mu.Lock() evicted[key] = value mu.Unlock() diff --git a/types/types.go b/types/types.go index bd94ceb..5572784 100644 --- a/types/types.go +++ b/types/types.go @@ -1,6 +1,19 @@ package types -type OnEvictCallback[K comparable, V any] func(key K, value V) +// EvictReason is the reason for an entry to be evicted from the cache. +// It is used in the [OnEvictCallback] function. +type EvictReason int + +const ( + // EvictReasonExpired is used when an item is removed because its TTL has expired. + EvictReasonExpired = iota + // EvictReasonEvicted is used when an item is removed because the cache size limit was exceeded. + EvictReasonEvicted + // EvictReasonRemoved is used when an item is explicitly deleted. + EvictReasonRemoved +) + +type OnEvictCallback[K comparable, V any] func(key K, value V, reason EvictReason) // Cache is the interface for a cache. type Cache[K comparable, V any] interface {