zcache

package
v0.0.0-...-ff885c8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 24, 2026 License: Apache-2.0 Imports: 14 Imported by: 1

README

zcache Package

Overview

The zcache package provides an abstraction layer over Redis, allowing easy integration of caching mechanisms into Go applications. It simplifies interacting with Redis by offering a common interface for various caching operations.

Table of Contents

  1. Features
  2. Installation
  3. Usage
  4. Configuration
  5. Mocking Support

Features

  • Unified Caching Interface: Offers a consistent API for common caching operations, abstracting the complexity of direct Redis interactions.
  • Distributed Mutex Locks: Supports distributed synchronization using Redis-based mutex locks, crucial for concurrent operations.
  • Extensibility: Easy to extend with additional methods for more Redis operations.
  • Serialization and Deserialization: Automatically handles the conversion of Go data structures to and from Redis storage formats.
  • Mocking for Testing: Includes mock implementations for easy unit testing without a live Redis instance.
  • Connection Pool Management: Efficiently handles Redis connection pooling.
  • Supported Operations: Includes a variety of caching operations like Set, Get, Delete, as well as more advanced operations like Incr, Decr, and others.

Installation

go get github.com/zondax/golem/pkg/zcache

Usage Remote cache - Redis

import (
    "github.com/zondax/golem/pkg/zcache"
    "context"
    "time"
)

func main() {
    config := zcache.RemoteConfig{Addr: "localhost:6379"}
    cache := zcache.NewRemoteCache(config)
    ctx := context.Background()

    // Set a value
    cache.Set(ctx, "key1", "value1", 10*time.Minute)

    // Get a value
    if value, err := cache.Get(ctx, "key1"); err == nil {
        fmt.Println("Retrieved value:", value)
    }

    // Delete a value
    cache.Delete(ctx, "key1")
}

Usage Local cache - Ristretto

The LocalConfig for zcache provides configuration for the Ristretto cache. Ristretto is a high-performance memory-bound cache with built-in metrics and automatic memory management.

It's important to note that MetricServer is a mandatory configuration field in LocalConfig to facilitate the monitoring of cache operations and errors.

func main() {
    config := zcache.LocalConfig{
        // Ristretto cache configuration
        NumCounters: 1e7,         // Number of keys to track frequency (default: 10M)
        MaxCostMB:   1024,        // Maximum cost of cache in MB (default: 1024MB/1GB)
        BufferItems: 64,          // Number of keys per Get buffer (default: 64)
        
        // Metrics are required
        MetricServer: metricServer, 
    }
    
    cache, err := zcache.NewLocalCache(&config)
    if err != nil {
        // Handle error
    }
    
    ctx := context.Background()
    
    cache.Set(ctx, "key1", "value1", 10*time.Minute)
    if value, err := cache.Get(ctx, "key1"); err == nil {
        fmt.Println("Retrieved value:", value)
    }
    cache.Delete(ctx, "key1")
}

Usage Combined cache - Local and Remote

func main() {
    localConfig := zcache.LocalConfig{
        // Ristretto cache configuration
        NumCounters: 1e7,          // Number of keys to track (default: 10M)
        MaxCostMB:   512,          // Max memory usage - 512MB
        BufferItems: 64,           // Size of Get buffer
        
        // Metrics are required
        MetricServer: metricServer,
    }
    remoteConfig := zcache.RemoteConfig{Addr: "localhost:6379"}
    config := zcache.CombinedConfig{Local: localConfig, Remote: remoteConfig, isRemoteBestEffort: false}
    cache, err := zcache.NewCombinedCache(config)
    if err != nil {
        // Handle error
    }
    
    ctx := context.Background()
    
    cache.Set(ctx, "key1", "value1", 10*time.Minute)
    if value, err := cache.Get(ctx, "key1"); err == nil {
        fmt.Println("Retrieved value:", value)
    }
    cache.Delete(ctx, "key1")
}


Configuration

Configure zcache using the Config struct, which includes network settings, server address, timeouts, and other connection parameters. This struct allows you to customize the behavior of your cache and mutex instances to fit your application's needs.

type Config struct {
    Addr             string        // Redis server address
    Password         string        // Redis server password
    DB               int           // Redis database
    DialTimeout      time.Duration // Timeout for connecting to Redis
    ReadTimeout      time.Duration // Timeout for reading from Redis
    WriteTimeout     time.Duration // Timeout for writing to Redis
    PoolSize         int           // Number of connections in the pool
    MinIdleConns     int           // Minimum number of idle connections
    IdleTimeout      time.Duration // Timeout for idle connections
}

Working with mutex

func main() {
    cache := zcache.NewCache(zcache.Config{Addr: "localhost:6379"})
    mutex := cache.NewMutex("mutex_name", 2*time.Minute)

    // Acquire lock
    if err := mutex.Lock(); err != nil {
        log.Fatalf("Failed to acquire mutex: %v", err)
    }

    // Perform operations under lock
    // ...

    // Release lock
    if ok, err := mutex.Unlock(); !ok || err != nil {
        log.Fatalf("Failed to release mutex: %v", err)
    }
}

Mocking support

Use MockZCache and MockZMutex for unit testing.

func TestCacheOperation(t *testing.T) {
    mockCache := new(zcache.MockZCache)
    mockCache.On("Get", mock.Anything, "key1").Return("value1", nil)
    // Use mockCache in your tests
}

func TestSomeFunctionWithMutex(t *testing.T) {
    mockMutex := new(zcache.MockZMutex)
    mockMutex.On("Lock").Return(nil)
    mockMutex.On("Unlock").Return(true, nil)
    mockMutex.On("Name").Return("myMutex")
    
    result, err := SomeFunctionThatUsesMutex(mockMutex)
    assert.NoError(t, err)
    assert.Equal(t, expectedResult, result)
    
    mockMutex.AssertExpectations(t)
}

Best Practices - Ristretto Cache

Memory Management

When using the local cache (Ristretto), memory is managed efficiently:

  1. Memory Control:

    • Ristretto uses precise memory tracking with a cost-based system
    • Items are evicted based on cost, access frequency, and recency
    • Built-in admission policy prevents low-value items from entering the cache
  2. Configuration Parameters:

    • NumCounters: Number of keys to track (default: 1e7 or 10 million)
    • MaxCostMB: Maximum memory in MB (default: 1024MB or 1GB)
    • BufferItems: Size of the Get buffer for handling concurrent operations (default: 64)
  3. TTL Behavior:

    • TTL is handled internally by Ristretto
    • Setting ttl <= 0 in the API means the item never expires
    • Ristretto automatically removes expired items
Memory Monitoring

Monitor cache performance through Ristretto metrics. The following metrics are available:

  • localCacheHitsMetricName: Number of cache hits
  • localCacheMissesMetricName: Number of cache misses
  • localCacheDelHitsMetricName: Number of successful deletions
  • localCacheDelMissesMetricName: Number of failed deletions
  • localCacheCollisionsMetricName: Number of key collisions

For Redis metrics:

  • remoteCachePoolHitsMetricName: Free connection found in the pool
  • remoteCachePoolMissesMetricName: Free connection not found in the pool
  • remoteCachePoolTimeoutsMetricName: Wait timeout occurrences
  • remoteCachePoolTotalConnsMetricName: Total connections in the pool
  • remoteCachePoolIdleConnsMetricName: Idle connections in the pool
  • remoteCachePoolStaleConnsMetricName: Stale connections removed
Best Practices
  1. Memory Configuration:

    • Set appropriate NumCounters based on expected number of keys (~10x the items)
    • Configure MaxCostMB based on available system memory (in megabytes)
    • Adjust BufferItems for high concurrency scenarios (default 64 is suitable for most cases)
  2. Production Recommendations:

    • Use Combined Cache with Redis for persistence
    • Monitor hit ratios through the exposed metrics
    • Set appropriate TTLs for data freshness
    • For critical production systems, implement fallback mechanisms
  3. Cache Tuning:

    • For high-throughput systems, increase BufferItems
    • For memory-constrained environments, decrease MaxCostMB appropriately
    • Keep NumCounters at approximately 10x your expected item count for optimal hit ratio
Notes
  • Ristretto provides better memory management than the previously used BigCache
  • No known memory leak issues or required manual cleanup processes
  • Predictable memory usage with automatic item eviction
  • Better performance under high load with concurrent operations
  • Cost-based eviction allows prioritizing important cache items

Documentation

Index

Constants

View Source
const (
	// Default Ristretto cache config
	DefaultNumCounters = int64(1e7)  // 10M keys
	DefaultMaxCostMB   = int64(1024) // 1GB
	DefaultBufferItems = int64(64)
)
View Source
const KeySplitter = "/"

Variables

This section is empty.

Functions

This section is empty.

Types

type CacheItem

type CacheItem struct {
	Value     []byte `json:"value"`
	ExpiresAt int64  `json:"expires_at"`
}

func NewCacheItem

func NewCacheItem(value []byte, ttl time.Duration) CacheItem

func (CacheItem) IsExpired

func (item CacheItem) IsExpired() bool

type CombinedCache

type CombinedCache interface {
	ZCache
}

func NewCombinedCache

func NewCombinedCache(combinedConfig *CombinedConfig) (CombinedCache, error)

type CombinedConfig

type CombinedConfig struct {
	Local              *LocalConfig
	Remote             *RemoteConfig
	GlobalLogger       *logger.Logger
	GlobalPrefix       string
	GlobalMetricServer metrics.TaskMetrics
	GlobalStatsMetrics StatsMetrics
	IsRemoteBestEffort bool
}

type CustomZ

type CustomZ struct {
	Score  float64
	Member any
}

type LocalCache

type LocalCache interface {
	ZCache
}

func NewLocalCache

func NewLocalCache(config *LocalConfig) (LocalCache, error)

type LocalConfig

type LocalConfig struct {
	Prefix       string
	Logger       *logger.Logger
	MetricServer metrics.TaskMetrics
	StatsMetrics StatsMetrics

	// Add Ristretto cache configuration
	NumCounters int64 `json:"num_counters"` // default: 1e7
	MaxCostMB   int64 `json:"max_cost_mb"`  // in MB, default: 1024 (1GB)
	BufferItems int64 `json:"buffer_items"` // default: 64
}

func (*LocalConfig) ToRistrettoConfig

func (c *LocalConfig) ToRistrettoConfig() *ristretto.Config

type MockZCache

type MockZCache struct {
	mock.Mock
}

func (*MockZCache) Decr

func (m *MockZCache) Decr(ctx context.Context, key string) (int64, error)

func (*MockZCache) Delete

func (m *MockZCache) Delete(ctx context.Context, key string) error

func (*MockZCache) Exists

func (m *MockZCache) Exists(ctx context.Context, keys ...string) (int64, error)

func (*MockZCache) Expire

func (m *MockZCache) Expire(ctx context.Context, key string, ttl time.Duration) (bool, error)

func (*MockZCache) FlushAll

func (m *MockZCache) FlushAll(ctx context.Context) error

func (*MockZCache) Get

func (m *MockZCache) Get(ctx context.Context, key string, data any) error

func (*MockZCache) GetStats

func (m *MockZCache) GetStats() ZCacheStats

func (*MockZCache) HGet

func (m *MockZCache) HGet(ctx context.Context, key, field string) (string, error)

func (*MockZCache) HSet

func (m *MockZCache) HSet(ctx context.Context, key string, values ...any) (int64, error)

func (*MockZCache) Incr

func (m *MockZCache) Incr(ctx context.Context, key string) (int64, error)

func (*MockZCache) IsNotFoundError

func (m *MockZCache) IsNotFoundError(err error) bool

func (*MockZCache) LPush

func (m *MockZCache) LPush(ctx context.Context, key string, values ...any) (int64, error)

func (*MockZCache) RPush

func (m *MockZCache) RPush(ctx context.Context, key string, values ...any) (int64, error)

func (*MockZCache) SAdd

func (m *MockZCache) SAdd(ctx context.Context, key string, members ...any) (int64, error)

func (*MockZCache) SMembers

func (m *MockZCache) SMembers(ctx context.Context, key string) ([]string, error)

func (*MockZCache) Set

func (m *MockZCache) Set(ctx context.Context, key string, value any, ttl time.Duration) error

func (*MockZCache) SetupAndMonitorMetrics

func (m *MockZCache) SetupAndMonitorMetrics(appName string, metricsServer metrics.TaskMetrics, updateInterval time.Duration) []error

func (*MockZCache) TTL

func (m *MockZCache) TTL(ctx context.Context, key string) (time.Duration, error)

func (*MockZCache) ZIncrBy

func (m *MockZCache) ZIncrBy(ctx context.Context, key string, member string, increment float64) (float64, error)

func (*MockZCache) ZRevRangeWithScores

func (m *MockZCache) ZRevRangeWithScores(ctx context.Context, key string, start, stop int64) ([]CustomZ, error)

type MockZMutex

type MockZMutex struct {
	mock.Mock
}

func (*MockZMutex) Lock

func (m *MockZMutex) Lock() error

func (*MockZMutex) Name

func (m *MockZMutex) Name() string

func (*MockZMutex) Unlock

func (m *MockZMutex) Unlock() (bool, error)

type RedisStats

type RedisStats struct {
	Pool *redis.PoolStats
}

type RemoteCache

type RemoteCache interface {
	ZCache
	Incr(ctx context.Context, key string) (int64, error)
	Decr(ctx context.Context, key string) (int64, error)
	LPush(ctx context.Context, key string, values ...any) (int64, error)
	RPush(ctx context.Context, key string, values ...any) (int64, error)
	SMembers(ctx context.Context, key string) ([]string, error)
	SAdd(ctx context.Context, key string, members ...any) (int64, error)
	HSet(ctx context.Context, key string, values ...any) (int64, error)
	HGet(ctx context.Context, key, field string) (string, error)
	ZIncrBy(ctx context.Context, key string, member string, increment float64) (float64, error)
	ZRevRangeWithScores(ctx context.Context, key string, start, stop int64) ([]CustomZ, error)
	FlushAll(ctx context.Context) error
	Exists(ctx context.Context, keys ...string) (int64, error)
	Expire(ctx context.Context, key string, ttl time.Duration) (bool, error)
	TTL(ctx context.Context, key string) (time.Duration, error)
}

func NewRemoteCache

func NewRemoteCache(config *RemoteConfig) (RemoteCache, error)

type RemoteConfig

type RemoteConfig struct {
	Network            string
	Addr               string
	Password           string
	DB                 int
	DialTimeout        time.Duration
	ReadTimeout        time.Duration
	WriteTimeout       time.Duration
	PoolSize           int
	MinIdleConns       int
	MaxConnAge         time.Duration
	PoolTimeout        time.Duration
	IdleTimeout        time.Duration
	IdleCheckFrequency time.Duration
	Prefix             string
	Logger             *logger.Logger
	MetricServer       metrics.TaskMetrics
	StatsMetrics       StatsMetrics
}

func (*RemoteConfig) ToRedisConfig

func (c *RemoteConfig) ToRedisConfig() *redis.Options

type StatsMetrics

type StatsMetrics struct {
	Enable         bool
	UpdateInterval time.Duration
}

type ZCache

type ZCache interface {
	Set(ctx context.Context, key string, value any, ttl time.Duration) error
	Get(ctx context.Context, key string, data any) error
	Delete(ctx context.Context, key string) error
	GetStats() ZCacheStats
	IsNotFoundError(err error) bool
}

type ZCacheStats

type ZCacheStats struct {
	Local  *ristretto.Metrics
	Remote *RedisStats
}

type ZMutex

type ZMutex interface {
	Lock() error
	Unlock() (bool, error)
	Name() string
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL