dgraph-io / ristretto

A high performance memory-bound Go cache

Home Page:https://dgraph.io/blog/post/introducing-ristretto-high-perf-go-cache/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Update returns false even when the item was updated

jarifibrahim opened this issue · comments

The bug was seen in dgraph-io/badger#1350 .
See dgraph-io/badger#1350 (comment) for a detailed explanation.

The following TestUpdateBug test fails on current master a093fe6

diff --git a/cache.go b/cache.go
index e48a48a..c5e19f0 100644
--- a/cache.go
+++ b/cache.go
@@ -29,7 +29,7 @@ import (
 	"github.com/dgraph-io/ristretto/z"
 )
 
-const (
+var (
 	// TODO: find the optimal value for this or make it configurable
 	setBufSize = 32 * 1024
 )
diff --git a/cache_test.go b/cache_test.go
index c1dc745..7c0bd9b 100644
--- a/cache_test.go
+++ b/cache_test.go
@@ -1,7 +1,9 @@
 package ristretto
 
 import (
+	"fmt"
 	"math/rand"
+	"strconv"
 	"strings"
 	"sync"
 	"testing"
@@ -614,3 +616,54 @@ func init() {
 	// Set bucketSizeSecs to 1 to avoid waiting too much during the tests.
 	bucketDurationSecs = 1
 }
+
+func TestUpdateBug(t *testing.T) {
+	originalSetBugSize := setBufSize
+	defer func() { setBufSize = originalSetBugSize }()
+	test := func() {
+		droppedMap := make(map[int]struct{})
+		lastEvictedSet := int64(-1)
+
+		var err error
+		handler := func(_ interface{}, value interface{}) {
+			v := value.(string)
+			lastEvictedSet, err = strconv.ParseInt(string(v), 10, 32)
+			require.NoError(t, err)
+			_, ok := droppedMap[int(lastEvictedSet)]
+			if ok {
+				panic(fmt.Sprintf("val = %+v was dropped but got evicted. Dropped items: %+v\n", lastEvictedSet, droppedMap))
+			}
+		}
+
+		// This is important.
+		setBufSize = 10
+
+		c, err := NewCache(&Config{
+			NumCounters: 100,
+			MaxCost:     10,
+			BufferItems: 64,
+			Metrics:     true,
+			OnEvict: func(_, _ uint64, value interface{}, _ int64) {
+				handler(nil, value)
+			},
+		})
+		require.NoError(t, err)
+
+		for i := 0; i < 5*setBufSize; i++ {
+			v := fmt.Sprintf("%0100d", i)
+			// We're updating the same key.
+			if !c.Set(0, v, 1) {
+				time.Sleep(time.Microsecond)
+				// fmt.Println("failed", i)
+				droppedMap[i] = struct{}{}
+			}
+		}
+		time.Sleep(time.Millisecond)
+		require.True(t, c.Set(1, nil, 10))
+		c.Close()
+	}
+	// Run the test 100 times.
+	for i := 0; i < 100; i++ {
+		test()
+	}
+}