Skip to content

Commit

Permalink
[Chore] Add pinned weight
Browse files Browse the repository at this point in the history
  • Loading branch information
maypok86 committed Sep 1, 2024
1 parent d2154ae commit 592f9d7
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 18 deletions.
3 changes: 3 additions & 0 deletions builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ func (b *Builder[K, V]) InitialCapacity(initialCapacity int) *Builder[K, V] {
// of this method requires a corresponding call to MaximumWeight prior to calling Build.
// Weights are measured and recorded when entries are inserted into or updated in
// the cache, and are thus effectively static during the lifetime of a cache entry.
//
// When the weight of an entry is zero it will not be considered for size-based eviction (though
// it still may be evicted by other means).
func (b *Builder[K, V]) Weigher(weigher func(key K, value V) uint32) *Builder[K, V] {
b.weigher = weigher
return b
Expand Down
43 changes: 25 additions & 18 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func (dc DeletionCause) String() string {

const (
minWriteBufferSize uint32 = 4
pinnedWeight uint32 = 0
)

var (
Expand Down Expand Up @@ -436,6 +437,23 @@ func (c *Cache[K, V]) evictNode(n node.Node[K, V]) {
}
}

func (c *Cache[K, V]) addToPolicies(n node.Node[K, V]) {
if !n.IsAlive() {
return
}

c.expiryPolicy.Add(n)
if n.Weight() != pinnedWeight {
c.policy.Add(n, c.clock.Offset())
}
}

func (c *Cache[K, V]) deleteFromPolicies(n node.Node[K, V], cause DeletionCause) {
c.expiryPolicy.Delete(n)
c.policy.Delete(n)
c.notifyDeletion(n.Key(), n.Value(), cause)
}

func (c *Cache[K, V]) onWrite(t task[K, V]) {
if t.isClear() || t.isClose() {
c.writeBuffer.Clear()
Expand All @@ -453,27 +471,16 @@ func (c *Cache[K, V]) onWrite(t task[K, V]) {
n := t.node()
switch {
case t.isAdd():
if n.IsAlive() {
c.expiryPolicy.Add(n)
c.policy.Add(n, c.clock.Offset())
}
c.addToPolicies(n)
case t.isUpdate():
oldNode := t.oldNode()
c.expiryPolicy.Delete(oldNode)
c.policy.Delete(oldNode)
if n.IsAlive() {
c.expiryPolicy.Add(n)
c.policy.Add(n, c.clock.Offset())
}
c.notifyDeletion(oldNode.Key(), oldNode.Value(), Replaced)
c.deleteFromPolicies(t.oldNode(), Replaced)
c.addToPolicies(n)
case t.isDelete():
c.expiryPolicy.Delete(n)
c.policy.Delete(n)
c.notifyDeletion(n.Key(), n.Value(), Explicit)
c.deleteFromPolicies(n, Explicit)
case t.isExpired():
c.expiryPolicy.Delete(n)
c.policy.Delete(n)
c.notifyDeletion(n.Key(), n.Value(), Expired)
c.deleteFromPolicies(n, Expired)
default:
panic("invalid task type")
}
}

Expand Down
66 changes: 66 additions & 0 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,72 @@ func TestCache_Unbounded(t *testing.T) {
if m[Replaced] != replaced {
t.Fatalf("cache was supposed to replace %d, but replaced %d entries", replaced, m[Replaced])
}
if hitRatio := statsCounter.Snapshot().HitRatio(); hitRatio != 0.5 {
t.Fatalf("not valid hit ratio. expected %.2f, but got %.2f", 0.5, hitRatio)
}
}

func TestCache_PinnedWeight(t *testing.T) {
size := 10
pinned := 4
m := make(map[DeletionCause]int)
mutex := sync.Mutex{}
c, err := NewBuilder[int, int]().
MaximumWeight(uint64(size)).
Weigher(func(key int, value int) uint32 {
if key == pinned {
return pinnedWeight
}
return 1
}).
WithTTL(3 * time.Second).
DeletionListener(func(key int, value int, cause DeletionCause) {
mutex.Lock()
m[cause]++
mutex.Unlock()
}).
Build()
if err != nil {
t.Fatalf("can not create cache: %v", err)
}

for i := 0; i < size; i++ {
c.Set(i, i)
}
for i := 0; i < size; i++ {
if !c.Has(i) {
t.Fatalf("the key must exist: %d", i)
}
c.Has(i)
}
for i := size; i < 2*size; i++ {
c.Set(i, i)
}
time.Sleep(time.Second)
for i := size; i < 2*size; i++ {
if !c.Has(i) {
t.Fatalf("the key must exist: %d", i)
}
c.Has(i)
}
if !c.Has(pinned) {
t.Fatalf("the key must exist: %d", pinned)
}

time.Sleep(3 * time.Second)

if c.Has(pinned) {
t.Fatalf("the key must not exist: %d", pinned)
}

mutex.Lock()
defer mutex.Unlock()
if len(m) != 2 || m[Size] != size-1 {
t.Fatalf("cache was supposed to evict %d, but evicted %d entries", size-1, m[Size])
}
if m[Expired] != size+1 {
t.Fatalf("cache was supposed to expire %d, but expired %d entries", size+1, m[Expired])
}
}

func TestCache_SetWithWeight(t *testing.T) {
Expand Down

0 comments on commit 592f9d7

Please sign in to comment.