FreeCache

repository·master·Indexed 26 days ago

https://github.com/coocood/freecache

A high-performance Go cache library designed to store hundreds of millions of entries with zero GC overhead. It utilizes a sharded architecture and minimized pointer usage to reduce garbage collection impact. Key features include support for byte slice and int64 keys, atomic updates via Update(), zero-copy access with GetFn and PeekFn, and a Redis-compatible server implementation.

Tokens
1.7K
Snippets
1
Records
15
Agent score
89%

What's inside freecache

  1. Initialize and use FreeCache

    master

    To use FreeCache, initialize a new cache instance using freecache.NewCache(cacheSize) where cacheSize is the total memory allocated in bytes.

    Key operations include:

    • Set(key, val, expire): Stores a byte slice value with a specified expiration time in seconds.
    • Get(key): Retrieves the value associated with the key. Returns the value and an error if the key is not found.
    • Del(key): Deletes a key. Returns the number of affected entries.
    • EntryCount(): Returns the current number of entries in the cache.

    Note on Expiration: If you set an expiration of X seconds, the actual duration will be within (X-1, X] seconds because sub-second precision is ignored during expiration calculation.

    // In bytes, where 1024 * 1024 represents a single Megabyte, and 100 * 1024*1024 represents 100 Megabytes.
    cacheSize := 100 * 1024 * 1024
    cache := freecache.NewCache(cacheSize)
    
    // Adjust GC frequency if allocating large amounts of memory
    debug.SetGCPercent(20)
    
    key := []byte("abc")
    val := []byte("def")
    expire := 60 // expire in 60 seconds
    
    // Set a value
    cache.Set(key, val, expire)
    
    // Get a value
    got, err := cache.Get(key)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("%s\n", got)
    }
    
    // Delete a value
    affected := cache.Del(key)
    fmt.Println("deleted key ", affected)
    fmt.Println("entry count ", cache.EntryCount())
  2. Initialize a new FreeCache instance

    master

    Use NewCache(size int) to create a new cache instance with a specified size in bytes. The minimum size is 512KB.

    Note on Memory Management: If you allocate a large cache, it is recommended to call debug.SetGCPercent() with a smaller value to limit memory consumption and GC pause times.

  3. Optimize GC performance with FreeCache

    master
    Because FreeCache preallocates memory, you may need to adjust the Go garbage collector settings to maintain normal GC frequency when using large cache sizes. It is recommended to use debug.SetGCPercent() with a lower percentage (e.g., debug.SetGCPercent(20)) to manage the impact of the large preallocated memory block.
  4. Iterate through cache entries using NewIterator

    master
    To traverse all entries currently stored in the cache, use the NewIterator method on your Cache instance. The iterator provides a Next() method to retrieve entries one by one. Note that the order of entries returned by the iterator is not guaranteed. When Next() returns nil, all entries have been traversed.
  5. Retrieve multiple keys with MultiGet

    master

    Use MultiGet(keys [][]byte) to fetch multiple values efficiently. It reduces lock contention by grouping keys by segment and acquiring each segment lock only once.

    Warning: MultiGet holds segment locks longer than a single Get, which may increase tail latency for concurrent Get calls.

  6. Perform atomic updates with Update()

    master

    The Update method allows for an atomic Get-and-Set operation. It retrieves the current value and passes it to an Updater function. The Updater decides whether to replace the value and what the new expiration should be.

    Updater signature: func(value []byte, found bool) (newValue []byte, replace bool, expireSeconds int)

  7. Initialize a FreeCache server

    master
    Use NewServer(cacheSize int) to create a new Server instance. The cacheSize parameter defines the total memory allocated for the cache in bytes. The resulting server wraps a freecache.Cache instance and is ready to be started via the Start method.
  8. Work with integer keys

    master

    FreeCache provides helper methods to use int64 as keys, which are internally converted to 8-byte little-endian byte slices.

    • SetInt(key int64, value []byte, expireSeconds int)
    • GetInt(key int64) ([]byte, error)
    • GetIntWithExpiration(key int64) ([]byte, uint32, error)
    • DelInt(key int64) bool
  9. Use zero-copy access with GetFn and PeekFn

    master

    To avoid memory allocations, use GetFn or PeekFn. These methods provide a slice view directly over the underlying memory.

    • GetFn(key []byte, fn func([]byte) error): Provides a zero-copy view of the value. The function fn is called with the slice. If the value wraps around the segment ring buffer, an allocation may occur.
    • PeekFn(key []byte, fn func([]byte) error): Similar to GetFn, but does not perform expiry checks. It may return expired values.
  10. Set and Get cache entries

    master

    Store and retrieve data using byte slices as keys and values.

    • Set(key, value []byte, expireSeconds int): Stores an entry. expireSeconds <= 0 means no expiration (but the entry can still be evicted if the cache is full).
      • Constraints: Keys must be $\le$ 65535 bytes. Values must be $\le$ 1/1024 of the total cache size. If these constraints are violated, the entry will not be written.
    • Get(key []byte): Returns the value or an error if not found.
    • GetWithBuf(key, buf []byte): Copies the value into an existing buffer buf. This avoids allocation if cap(buf) is sufficient.