bigcache icon indicating copy to clipboard operation
bigcache copied to clipboard

panic: runtime error: makeslice: len out of range

Open turfaa opened this issue 5 years ago • 1 comments

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-release or winver.exe): Ubuntu 16.04.6 LTS
  • go version: 1.15

turfaa avatar Nov 26 '20 07:11 turfaa


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
}

ljluestc avatar May 19 '25 18:05 ljluestc