Skip to content

Commit

Permalink
[Chore] Move the rejects to the worker and start returning values fro…
Browse files Browse the repository at this point in the history
…m Set and Delete
  • Loading branch information
maypok86 committed Sep 1, 2024
1 parent 592f9d7 commit fb42aec
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 41 deletions.
74 changes: 46 additions & 28 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,6 @@ func (c *Cache[K, V]) afterGet(got node.Node[K, V]) {
}
}

// Set associates the value with the key in this cache.
//
// If it returns false, then the key-value item had too much weight and the Set was dropped.
func (c *Cache[K, V]) Set(key K, value V) bool {
return c.set(key, value, c.defaultExpiration(), false)
}

func (c *Cache[K, V]) defaultExpiration() int64 {
if c.ttl == 0 {
return 0
Expand All @@ -289,53 +282,61 @@ func (c *Cache[K, V]) defaultExpiration() int64 {
return c.getExpiration(c.ttl)
}

// Set associates the value with the key in this cache.
//
// If the specified key is not already associated with a value, then it returns new value and true.
//
// If the specified key is already associated with a value, then it returns existing value and false.
func (c *Cache[K, V]) Set(key K, value V) (V, bool) {
return c.set(key, value, c.defaultExpiration(), false)
}

// SetWithTTL associates the value with the key in this cache and sets the custom ttl for this key-value item.
//
// If it returns false, then the key-value item had too much weight and the SetWithTTL was dropped.
func (c *Cache[K, V]) SetWithTTL(key K, value V, ttl time.Duration) bool {
// If the specified key is not already associated with a value, then it returns new value and true.
//
// If the specified key is already associated with a value, then it returns existing value and false.
func (c *Cache[K, V]) SetWithTTL(key K, value V, ttl time.Duration) (V, bool) {
return c.set(key, value, c.getExpiration(ttl), false)
}

// SetIfAbsent if the specified key is not already associated with a value associates it with the given value.
//
// If the specified key is not already associated with a value, then it returns false.
// If the specified key is not already associated with a value, then it returns new value and true.
//
// Also, it returns false if the key-value item had too much weight and the SetIfAbsent was dropped.
func (c *Cache[K, V]) SetIfAbsent(key K, value V) bool {
// If the specified key is already associated with a value, then it returns existing value and false.
func (c *Cache[K, V]) SetIfAbsent(key K, value V) (V, bool) {
return c.set(key, value, c.defaultExpiration(), true)
}

// SetIfAbsentWithTTL if the specified key is not already associated with a value associates it with the given value
// and sets the custom ttl for this key-value item.
//
// If the specified key is not already associated with a value, then it returns false.
// If the specified key is not already associated with a value, then it returns new value and true.
//
// Also, it returns false if the key-value item had too much weight and the SetIfAbsent was dropped.
func (c *Cache[K, V]) SetIfAbsentWithTTL(key K, value V, ttl time.Duration) bool {
// If the specified key is already associated with a value, then it returns existing value and false.
func (c *Cache[K, V]) SetIfAbsentWithTTL(key K, value V, ttl time.Duration) (V, bool) {
return c.set(key, value, c.getExpiration(ttl), true)
}

func (c *Cache[K, V]) set(key K, value V, expiration int64, onlyIfAbsent bool) bool {
weight := c.weigher(key, value)
if uint64(weight) > c.policy.MaxAvailableWeight() {
c.stats.CollectRejectedSets(1)
return false
}

n := c.nodeManager.Create(key, value, expiration, weight)
func (c *Cache[K, V]) set(key K, value V, expiration int64, onlyIfAbsent bool) (V, bool) {
n := c.nodeManager.Create(key, value, expiration, c.weigher(key, value))
if onlyIfAbsent {
res := c.hashmap.SetIfAbsent(n)
if res == nil {
c.afterWrite(n, nil)
return true
return value, true
}
return false
return res.Value(), false
}

evicted := c.hashmap.Set(n)
c.afterWrite(n, evicted)

return true
if evicted != nil {
return evicted.Value(), false
}
return value, true
}

func (c *Cache[K, V]) afterWrite(n, evicted node.Node[K, V]) {
Expand All @@ -357,8 +358,16 @@ func (c *Cache[K, V]) afterWrite(n, evicted node.Node[K, V]) {
}

// Delete deletes the association for this key from the cache.
func (c *Cache[K, V]) Delete(key K) {
c.afterDelete(c.hashmap.Delete(key))
//
// Returns previous value if any. The deleted result reports whether the key was
// present.
func (c *Cache[K, V]) Delete(key K) (value V, deleted bool) {
d := c.hashmap.Delete(key)
c.afterDelete(d)
if d != nil {
return d.Value(), true
}
return zeroValue[V](), false
}

func (c *Cache[K, V]) deleteNode(n node.Node[K, V]) {
Expand Down Expand Up @@ -442,6 +451,15 @@ func (c *Cache[K, V]) addToPolicies(n node.Node[K, V]) {
return
}

if uint64(n.Weight()) > c.policy.MaxAvailableWeight() {
deleted := c.hashmap.DeleteNode(n)
if deleted != nil {
n.Die()
}
c.stats.CollectRejectedSets(1)
return
}

c.expiryPolicy.Add(n)
if n.Weight() != pinnedWeight {
c.policy.Add(n, c.clock.Offset())
Expand Down
29 changes: 16 additions & 13 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,14 @@ func TestCache_PinnedWeight(t *testing.T) {
}

func TestCache_SetWithWeight(t *testing.T) {
statsCounter := stats.NewCounter()
size := uint64(10)
c, err := NewBuilder[uint32, int]().
MaximumWeight(size).
Weigher(func(key uint32, value int) uint32 {
return key
}).
CollectStats(statsCounter).
Build()
if err != nil {
t.Fatalf("can not create cache: %v", err)
Expand All @@ -170,20 +172,21 @@ func TestCache_SetWithWeight(t *testing.T) {
goodWeight := c.policy.MaxAvailableWeight()
badWeight := goodWeight + 1

added := c.Set(uint32(goodWeight), 1)
if !added {
t.Fatalf("Set was dropped, even though it shouldn't have been. Max available weight: %d, actual weight: %d",
c.policy.MaxAvailableWeight(),
c.weigher(uint32(goodWeight), 1),
)
}
added = c.Set(uint32(badWeight), 1)
if added {
c.Set(uint32(goodWeight), 1)
c.Set(uint32(badWeight), 1)
time.Sleep(time.Second)
if rejections := statsCounter.Snapshot().RejectedSets(); rejections != 1 {
t.Fatalf("Set wasn't dropped, though it should have been. Max available weight: %d, actual weight: %d",
c.policy.MaxAvailableWeight(),
c.weigher(uint32(badWeight), 1),
)
}
if !c.Has(uint32(goodWeight)) {
t.Fatalf("the key must exist: %d", goodWeight)
}
if c.Has(uint32(badWeight)) {
t.Fatalf("the key must not exist: %d", badWeight)
}
}

func TestCache_Range(t *testing.T) {
Expand Down Expand Up @@ -360,7 +363,7 @@ func TestCache_SetIfAbsent(t *testing.T) {
}

for i := 0; i < size; i++ {
if !c.SetIfAbsent(i, i) {
if _, ok := c.SetIfAbsent(i, i); !ok {
t.Fatalf("set was dropped. key: %d", i)
}
}
Expand All @@ -372,7 +375,7 @@ func TestCache_SetIfAbsent(t *testing.T) {
}

for i := 0; i < size; i++ {
if c.SetIfAbsent(i, i) {
if _, ok := c.SetIfAbsent(i, i); ok {
t.Fatalf("set wasn't dropped. key: %d", i)
}
}
Expand All @@ -389,7 +392,7 @@ func TestCache_SetIfAbsent(t *testing.T) {
}

for i := 0; i < size; i++ {
if !cc.SetIfAbsentWithTTL(i, i, time.Hour) {
if _, ok := cc.SetIfAbsentWithTTL(i, i, time.Hour); !ok {
t.Fatalf("set was dropped. key: %d", i)
}
}
Expand All @@ -401,7 +404,7 @@ func TestCache_SetIfAbsent(t *testing.T) {
}

for i := 0; i < size; i++ {
if cc.SetIfAbsentWithTTL(i, i, time.Second) {
if _, ok := cc.SetIfAbsentWithTTL(i, i, time.Second); ok {
t.Fatalf("set wasn't dropped. key: %d", i)
}
}
Expand Down

0 comments on commit fb42aec

Please sign in to comment.