Documentation
¶
Overview ¶
Package icache provides a high-performance, generic two-layer cache for Go.
Overview ¶
icache implements a two-layer caching strategy:
- L1: Fast local in-memory cache using ARC (Adaptive Replacement Cache)
- L2: Optional pluggable distributed cache backend (Redis, Memcached, DynamoDB, etc.)
Features ¶
- Backend Agnostic: Works with any storage backend through a simple interface
- Cache Invalidation: Automatic cache invalidation across multiple instances (if backend supports pub/sub)
- Cache Stampede Prevention: Built-in mutex protection
- Generic Support: Full Go generics support for type-safe operations
- Fetcher Functions: Automatic cache population on miss
- TTL Support: Flexible expiration times
Basic Usage ¶
Local cache only (no backend):
config := icache.Config{
LocalCacheSize: 10000,
InstanceID: "instance-1",
}
cache := icache.New(config)
defer cache.Close()
ctx := context.Background()
// Set a value
cache.Set(ctx, "key", "value", 5*time.Minute)
// Get a value
value, exists, err := icache.Get[string](cache, ctx, "key")
if err != nil {
log.Fatal(err)
}
if exists {
fmt.Println(value)
}
With Custom Backend ¶
To use a distributed backend, implement the Backend interface:
type Backend interface {
Get(ctx context.Context, key string) (string, bool, error)
Set(ctx context.Context, key string, value string, expiration time.Duration) error
Del(ctx context.Context, key string) error
TTL(ctx context.Context, key string) (time.Duration, error)
Publish(ctx context.Context, channel string, message string) error
Subscribe(ctx context.Context, channel string) (<-chan Message, error)
Close() error
}
Then use it with the cache:
backend := NewRedisBackend("localhost:6379", "", 0)
config := icache.Config{
Backend: backend,
LocalCacheSize: 10000,
InstanceID: "instance-1",
}
cache := icache.New(config)
defer cache.Close()
Fetcher Functions ¶
Use fetcher functions to automatically populate the cache on miss:
// Simple fetcher
value, exists, err := icache.Get(cache, ctx, "user:123", func() (User, error) {
return db.GetUser(123)
})
// Fetcher with expiration
value, exists, err := icache.Get(cache, ctx, "user:123", func() (User, time.Duration, error) {
user, err := db.GetUser(123)
return user, 5*time.Minute, err
})
Cache Invalidation ¶
When using a backend that supports pub/sub, cache invalidation is handled automatically:
- Instance 1 updates a key
- Backend publishes invalidation message
- Instance 2 receives message and clears its local cache
- Instance 2's next Get() fetches fresh data from backend
Performance ¶
- L1 Cache: ~100ns per operation (in-memory)
- L2 Cache: Depends on backend (typically ~1ms for Redis)
- Cache Hit Ratio: Typically 90%+ with proper TTL configuration
Index ¶
- func Get[T any](c *Cache, ctx context.Context, key string, fetcherOptions ...any) (T, bool, error)
- type Backend
- type Cache
- func (c *Cache) Close() error
- func (c *Cache) Del(ctx context.Context, key string) error
- func (c *Cache) GetAny(ctx context.Context, key string, fetcherOptions ...any) (any, bool, error)
- func (c *Cache) GetLocalKeys(ctx context.Context) []any
- func (c *Cache) LocalSet(ctx context.Context, key string, value any, expiration ...time.Duration) error
- func (c *Cache) Set(ctx context.Context, key string, value any, expiration ...time.Duration) error
- type Config
- type FetcherFunc
- type FetcherWithExpireFunc
- type Message
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Backend ¶
type Backend interface {
// Get retrieves a value from the backend
// Returns the value, a boolean indicating if the key exists, and an error
Get(ctx context.Context, key string) (string, bool, error)
// Set stores a value in the backend with optional expiration
// expiration of 0 means no expiration
Set(ctx context.Context, key string, value string, expiration time.Duration) error
// Del deletes a value from the backend
Del(ctx context.Context, key string) error
// TTL returns the time-to-live for a key
// Returns -1 if the key exists but has no expiration
// Returns -2 if the key does not exist
TTL(ctx context.Context, key string) (time.Duration, error)
// Publish publishes a message to a channel for cache invalidation
// Can be a no-op if you don't need multi-instance cache invalidation
Publish(ctx context.Context, channel string, message string) error
// Subscribe subscribes to a channel for cache invalidation messages
// Should return a channel that receives messages
// Can return nil if you don't need multi-instance cache invalidation
Subscribe(ctx context.Context, channel string) (<-chan Message, error)
// Close closes any connections to the backend
Close() error
}
Backend defines the interface for remote/distributed cache storage Implement this interface to use any storage backend (Redis, Memcached, DynamoDB, etc.)
type Cache ¶
type Cache struct {
// contains filtered or unexported fields
}
Cache represents a two-layer cache (L1: local, L2: backend)
func (*Cache) GetAny ¶
GetAny is a convenience method that returns any type For type-safe operations, use the generic Get[T] function
func (*Cache) GetLocalKeys ¶
GetLocalKeys returns all keys from the local cache
type Config ¶
type Config struct {
// Backend is the remote/distributed cache backend
// Implement the Backend interface to use any storage (Redis, Memcached, DynamoDB, etc.)
// Can be nil if you only want to use local cache
Backend Backend
// LocalCacheSize is the maximum number of items in the local cache
// Default: 10000
LocalCacheSize int
// InstanceID is a unique identifier for this instance
// Used for cache invalidation across multiple instances
// Only relevant if using a Backend with pub/sub support
InstanceID string
}
Config holds the configuration for the two-layer cache
type FetcherFunc ¶
FetcherFunc is a function that fetches a value when cache misses
type FetcherWithExpireFunc ¶
FetcherWithExpireFunc is a function that fetches a value with expiration when cache misses