Skip to content

Instantly share code, notes, and snippets.

@erikdubbelboer
Created October 29, 2019 15:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erikdubbelboer/bd904c51f00079421fd3eb2a061a50c0 to your computer and use it in GitHub Desktop.
Save erikdubbelboer/bd904c51f00079421fd3eb2a061a50c0 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"math/rand"
"net/http"
"runtime"
"strings"
"sync/atomic"
"time"
"unsafe"
"github.com/dgraph-io/ristretto"
_ "net/http/pprof"
)
const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
// key generates a random key.
func key() string {
b := make([]byte, 3) // 36^3 = 46656 possible keys
for i := 0; i < len(b); i++ {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
}
type Value struct {
key string
val string
}
var sizeofValue = int(unsafe.Sizeof(Value{}))
func (v *Value) cost() int64 {
return int64(len(v.key) + len(v.val) + sizeofValue)
}
func main() {
runtime.MemProfileRate = 1
start := time.Now()
cache, err := ristretto.NewCache(&ristretto.Config{
NumCounters: 466560, // Possible keys * 10
MaxCost: 1024 * 1024 * 1024, // 1 GiB
BufferItems: 64,
Metrics: false,
})
if err != nil {
panic(err)
}
hits := int64(0)
misses := int64(0)
// Speed things up by using 5 goroutines.
// Can also remove the time.Sleep or just wait longer.
for i := 0; i < 5; i++ {
go func() {
for {
// Sleep a little bit to give the cache some time to do things.
time.Sleep(time.Millisecond)
k := key()
v, ok := cache.Get(k) // Just doing Set without Get doesn't cause the bug to happen.
if ok && v != nil {
atomic.AddInt64(&hits, 1)
} else {
atomic.AddInt64(&misses, 1)
var val string
// This is important, only 1 size won't trigger the bug.
if rand.Intn(100) < 10 {
val = "test"
} else {
val = strings.Repeat("a", 1024*1024)
}
// If we uncomment this to insert as the same key as the miss above it doesn't happen.
k := key()
v := &Value{
key: k,
val: val,
}
cache.Set(k, v, v.cost())
}
}
}()
}
go func() {
panic(http.ListenAndServe("localhost:6060", nil))
}()
for {
time.Sleep(time.Second)
// Do a GC to get better allocation stats.
runtime.GC()
var m runtime.MemStats
runtime.ReadMemStats(&m)
hi := atomic.SwapInt64(&hits, 0)
mi := atomic.SwapInt64(&misses, 0)
fmt.Printf("time: %s alloc: %.2f MiB hits: %d misses: %d\n", time.Since(start).Round(time.Second), float64(m.Alloc)/1024/1024, hi, mi)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment