bigcache
bigcache copied to clipboard
panic: runtime error: makeslice: len out of range
What is the issue you are having? Panic when evicting the cache.
panic: runtime error: makeslice: len out of range
goroutine 1922 [running]:
github.com/allegro/bigcache/v3.readEntry(...)
/root/go/pkg/mod/github.com/allegro/bigcache/[email protected]/encoding.go:51
github.com/allegro/bigcache/v3.(*BigCache).providedOnRemoveWithReason(0xc00131c410, 0xc10359817d, 0x74, 0x199323, 0x1)
/root/go/pkg/mod/github.com/allegro/bigcache/[email protected]/bigcache.go:229 +0x188
github.com/allegro/bigcache/v3.(*cacheShard).removeOldestEntry(0xc0190fec60, 0x1, 0x0, 0x1b8fa72)
/root/go/pkg/mod/github.com/allegro/bigcache/[email protected]/shard.go:324 +0x102
github.com/allegro/bigcache/v3.(*cacheShard).onEvict(0xc0190fec60, 0xc10359817d, 0x74, 0x199323, 0x5fbf5b58, 0xc15827ae40, 0x0)
/root/go/pkg/mod/github.com/allegro/bigcache/[email protected]/shard.go:271 +0x5a
github.com/allegro/bigcache/v3.(*cacheShard).cleanUp(0xc0190fec60, 0x5fbf5b58)
/root/go/pkg/mod/github.com/allegro/bigcache/[email protected]/shard.go:282 +0xb6
github.com/allegro/bigcache/v3.(*BigCache).cleanUp(0xc00131c410, 0x5fbf5b58)
/root/go/pkg/mod/github.com/allegro/bigcache/[email protected]/bigcache.go:215 +0x53
github.com/allegro/bigcache/v3.newBigCache.func1(0x400, 0x61c9f36800, 0x1255dda3800, 0x927c0, 0x1f4, 0x100, 0x2f57ce0, 0x43c4b28, 0x2000, 0x0, ...)
/root/go/pkg/mod/github.com/allegro/bigcache/[email protected]/bigcache.go:92 +0x9b
created by github.com/allegro/bigcache/v3.newBigCache
/root/go/pkg/mod/github.com/allegro/bigcache/[email protected]/bigcache.go:86 +0x485
What is BigCache doing that it shouldn't? Panic
Minimal, Complete, and Verifiable Example
My service do a high number of get operations.
This is my config:
expiry := 7 * time.Minute
conf := bigcache.DefaultConfig(expiry)
conf.CleanWindow = expiry * 3
conf.HardMaxCacheSize = 8192
conf.OnRemoveWithReason = func(_ string, entry []byte, reason bigcache.RemoveReason) {
// monitoring
}
Environment:
- Version (git sha or release): v3.0.0
- OS (e.g. from
/etc/os-releaseor winver.exe): Ubuntu 16.04.6 LTS - go version: 1.15
package main
import (
"fmt"
"sync"
"time"
"github.com/allegro/bigcache/v3"
)
// CacheWithKeys wraps BigCache with a thread-safe key store
type CacheWithKeys struct {
cache *bigcache.BigCache
keys map[string]struct{}
keysLock sync.RWMutex
}
// NewCacheWithKeys initializes a new CacheWithKeys
func NewCacheWithKeys(config bigcache.Config) (*CacheWithKeys, error) {
// Wrap OnRemoveWithReason to catch panics
config.OnRemoveWithReason = func(key string, entry []byte, reason bigcache.RemoveReason) {
defer func() {
if r := recover(); r != nil {
fmt.Printf("Panic in OnRemoveWithReason: key=%s, reason=%v, error=%v\n", key, reason, r)
}
}()
// Monitoring
fmt.Printf("Removed key: %s, reason: %v\n", key, reason)
}
cache, err := bigcache.New(config)
if err != nil {
return nil, fmt.Errorf("failed to initialize BigCache: %v", err)
}
return &CacheWithKeys{
cache: cache,
keys: make(map[string]struct{}),
}, nil
}
// Set stores a key-value pair and updates the key store
func (c *CacheWithKeys) Set(key string, value []byte) error {
c.keysLock.Lock()
defer c.keysLock.Unlock()
if err := c.cache.Set(key, value); err != nil {
return fmt.Errorf("failed to set key %s: %v", key, err)
}
c.keys[key] = struct{}{}
return nil
}
// Get retrieves a value by key
func (c *CacheWithKeys) Get(key string) ([]byte, error) {
return c.cache.Get(key)
}
// Delete removes a key-value pair and updates the key store
func (c *CacheWithKeys) Delete(key string) error {
c.keysLock.Lock()
defer c.keysLock.Unlock()
if err := c.cache.Delete(key); err != nil {
return fmt.Errorf("failed to delete key %s: %v", key, err)
}
delete(c.keys, key)
return nil
}
// GetAllKeys returns all keys in the cache
func (c *CacheWithKeys) GetAllKeys() []string {
c.keysLock.RLock()
defer c.keysLock.RUnlock()
keys := make([]string, 0, len(c.keys))
for key := range c.keys {
keys = append(keys, key)
}
return keys
}
func main() {
// Configure BigCache
expiry := 7 * time.Minute
config := bigcache.DefaultConfig(expiry)
config.CleanWindow = expiry // Reduced to 7 minutes to clean more frequently
config.HardMaxCacheSize = 8192 // 8GB
config.Shards = 1024 // Default shard count
config.MaxEntriesInWindow = 1000 // Limit entries per shard
config.MaxEntrySize = 1024 // 1KB max entry size
config.Verbose = true // Enable logging
// Initialize CacheWithKeys
cache, err := NewCacheWithKeys(config)
if err != nil {
fmt.Printf("Error initializing cache: %v\n", err)
return
}
// Simulate high Get load
var wg sync.WaitGroup
numWorkers := 100
numOpsPerWorker := 1000
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
for j := 0; j < numOpsPerWorker; j++ {
key := fmt.Sprintf("key-%d-%d", workerID, j)
// Set entry
if err := cache.Set(key, []byte(fmt.Sprintf("value-%d-%d", workerID, j))); err != nil {
fmt.Printf("Worker %d: Set error: %v\n", workerID, err)
}
// Get entry
if _, err := cache.Get(key); err != nil {
fmt.Printf("Worker %d: Get error: %v\n", workerID, err)
}
// Small delay to simulate real workload
time.Sleep(time.Millisecond)
}
}(i)
}
// Periodically print keys
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
go func() {
for range ticker.C {
keys := cache.GetAllKeys()
fmt.Printf("Current keys (count=%d): %v\n", len(keys), keys[:min(5, len(keys))])
}
}()
// Wait for workers
wg.Wait()
// Final key count
keys := cache.GetAllKeys()
fmt.Printf("Final keys (count=%d): %v\n", len(keys), keys[:min(5, len(keys))])
}
// min returns the minimum of two integers
func min(a, b int) int {
if a < b {
return a
}
return b
}