cache

package
v1.35.1 Latest Latest
Warning

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

Go to latest
Published: Dec 12, 2025 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package cache provides a flexible caching abstraction with multiple backend implementations.

The cache package offers a unified interface for caching operations with support for:

  • In-memory caching (memoryCache)
  • Redis-based caching (redisCache)
  • Type-safe caching through generics (Typed[T])

Features:

  • Time-to-live (TTL) support for cache entries
  • Atomic operations for consistency
  • Concurrent-safe implementations
  • Configurable expiration policies
  • Support for custom serialization through the Item interface

Basic Usage:

// Create an in-memory cache with 1 hour default TTL
cache := cache.NewMemory(time.Hour)

// Set a value
err := cache.Set(ctx, "key", []byte("value"))
if err != nil {
    log.Fatal(err)
}

// Get a value
value, err := cache.Get(ctx, "key")
if err != nil {
    if errors.Is(err, cache.ErrKeyNotFound) {
        // Handle missing key
    } else if errors.Is(err, cache.ErrKeyExpired) {
        // Handle expired key
    } else {
        log.Fatal(err)
    }
}

// Set with custom TTL
err = cache.Set(ctx, "key", []byte("value"), cache.WithTTL(30*time.Minute))

// Set only if key doesn't exist
err = cache.SetOrFail(ctx, "key", []byte("value"))
if errors.Is(err, cache.ErrKeyExists) {
    // Key already exists
}

// Get and delete in one operation
value, err = cache.GetAndDelete(ctx, "key")

// Remove expired entries
err = cache.Cleanup(ctx)

// Get all items and clear the cache
items, err := cache.Drain(ctx)

// Close the cache when done
err = cache.Close()

Using Typed Cache:

// Define a type that implements the Item interface
type MyData struct {
    Field1 string
    Field2 int
}

func (d *MyData) Marshal() ([]byte, error) {
    return json.Marshal(d)
}

func (d *MyData) Unmarshal(data []byte) error {
    return json.Unmarshal(data, d)
}

// Create a typed cache
storage := cache.NewMemory(time.Hour)
typedCache := cache.NewTyped[*MyData](storage)

// Set typed value
data := &MyData{Field1: "test", Field2: 42}
err := typedCache.Set(ctx, "key", data)

// Get typed value
retrieved, err := typedCache.Get(ctx, "key")

Using Redis Cache:

// Create a Redis cache
config := cache.RedisConfig{
    URL:    "redis://localhost:6379",
    Prefix: "myapp:",
    TTL:    time.Hour,
}

redisCache, err := cache.NewRedis(config)
if err != nil {
    log.Fatal(err)
}
defer redisCache.Close()

// Use the same interface as memory cache
err = redisCache.Set(ctx, "key", []byte("value"))
value, err := redisCache.Get(ctx, "key")

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidConfig indicates an invalid configuration.
	ErrInvalidConfig = errors.New("invalid config")
	// ErrKeyNotFound indicates no value exists for the given key.
	//
	// This error is returned by Get operations when the requested key has never been
	// set in the cache or has been explicitly deleted. It is also returned by
	// GetAndDelete when the key does not exist.
	//
	// Example:
	//	_, err := cache.Get(ctx, "nonexistent-key")
	//	if errors.Is(err, cache.ErrKeyNotFound) {
	//	    // Handle missing key
	//	}
	ErrKeyNotFound = errors.New("key not found")

	// ErrKeyExpired indicates a value exists but has expired.
	//
	// This error is returned by Get operations when the requested key exists in the
	// cache but its time-to-live (TTL) has elapsed. Expired items may still exist
	// in the cache until they are explicitly removed by a Cleanup operation or
	// automatically by the cache implementation.
	//
	// Example:
	//	// Set a value with 1 second TTL
	//	cache.Set(ctx, "temp-key", []byte("data"), cache.WithTTL(time.Second))
	//	time.Sleep(2 * time.Second)
	//	_, err := cache.Get(ctx, "temp-key")
	//	if errors.Is(err, cache.ErrKeyExpired) {
	//	    // Handle expired key
	//	}
	ErrKeyExpired = errors.New("key expired")

	// ErrKeyExists indicates a conflicting set when the key already exists.
	//
	// This error is returned by SetOrFail operations when attempting to set a value
	// for a key that already exists in the cache and has not expired. This is useful
	// for implementing atomic "create if not exists" operations and preventing
	// race conditions in concurrent scenarios.
	//
	// Example:
	//	// Try to set a value only if key doesn't exist
	//	err := cache.SetOrFail(ctx, "lock-key", []byte("locked"))
	//	if errors.Is(err, cache.ErrKeyExists) {
	//	    // Key already exists, handle conflict
	//	}
	ErrKeyExists = errors.New("key already exists")

	ErrFailedToCreateZeroValue = errors.New("failed to create zero item")
)

Functions

This section is empty.

Types

type Cache

type Cache interface {
	// Set stores the value for the given key in the cache, overwriting any existing value.
	//
	// The value will be stored with the default TTL configured for the cache implementation,
	// unless overridden by options. If the key already exists, its value and TTL will be updated.
	//
	// Parameters:
	//   - ctx: Context for cancellation and timeouts
	//   - key: The key to store the value under
	//   - value: The value to store as a byte slice
	//   - opts: Optional configuration for this specific item (e.g., custom TTL)
	//
	// Returns:
	//   - error: nil on success, otherwise an error describing the failure
	//
	// Example:
	//	// Set with default TTL
	//	err := cache.Set(ctx, "user:123", []byte("user data"))
	//
	//	// Set with custom TTL
	//	err := cache.Set(ctx, "session:abc", []byte("session data"), cache.WithTTL(30*time.Minute))
	//
	//	// Set with specific expiration time
	//	expiration := time.Now().Add(2 * time.Hour)
	//	err := cache.Set(ctx, "temp:xyz", []byte("temp data"), cache.WithValidUntil(expiration))
	Set(ctx context.Context, key string, value []byte, opts ...Option) error

	// SetOrFail stores the value for the given key only if the key does not already exist.
	//
	// This is an atomic operation that prevents race conditions when multiple goroutines
	// might try to set the same key simultaneously. If the key exists but has expired,
	// it will be overwritten.
	//
	// Parameters:
	//   - ctx: Context for cancellation and timeouts
	//   - key: The key to store the value under
	//   - value: The value to store as a byte slice
	//   - opts: Optional configuration for this specific item (e.g., custom TTL)
	//
	// Returns:
	//   - error: nil on success, ErrKeyExists if the key already exists and is not expired,
	//            otherwise an error describing the failure
	//
	// Example:
	//	// Try to set a value only if key doesn't exist
	//	err := cache.SetOrFail(ctx, "lock:resource", []byte("locked"))
	//	if errors.Is(err, cache.ErrKeyExists) {
	//	    // Key already exists, handle conflict
	//	}
	SetOrFail(ctx context.Context, key string, value []byte, opts ...Option) error

	// Get retrieves the value for the given key from the cache.
	//
	// The behavior depends on the key's existence and expiration state:
	//   - If the key exists and has not expired, returns the value and nil error
	//   - If the key does not exist, returns nil and ErrKeyNotFound
	//   - If the key exists but has expired, returns nil and ErrKeyExpired
	//
	// GetOptions can be used to modify the behavior, such as updating TTL,
	// deleting the key after retrieval, or setting a new expiration time.
	//
	// Parameters:
	//   - ctx: Context for cancellation and timeouts
	//   - key: The key to retrieve
	//   - opts: Optional operations to perform during retrieval (e.g., AndDelete, AndSetTTL)
	//
	// Returns:
	//   - []byte: The cached value if found and not expired
	//   - error: nil on success, ErrKeyNotFound if key doesn't exist,
	//            ErrKeyExpired if key exists but has expired, otherwise an error
	//
	// Example:
	//	// Simple get
	//	value, err := cache.Get(ctx, "user:123")
	//
	//	// Get and extend TTL by 30 minutes
	//	value, err := cache.Get(ctx, "session:abc", cache.AndUpdateTTL(30*time.Minute))
	//
	//	// Get and delete atomically
	//	value, err := cache.Get(ctx, "temp:xyz", cache.AndDelete())
	Get(ctx context.Context, key string, opts ...GetOption) ([]byte, error)

	// GetAndDelete retrieves the value for the given key and atomically deletes it from the cache.
	//
	// This is equivalent to calling Get with the AndDelete option, but provides a more
	// convenient API for the common pattern of reading and removing a value in one operation.
	//
	// Parameters:
	//   - ctx: Context for cancellation and timeouts
	//   - key: The key to retrieve and delete
	//
	// Returns:
	//   - []byte: The cached value if found and not expired
	//   - error: nil on success, ErrKeyNotFound if key doesn't exist,
	//            ErrKeyExpired if key exists but has expired, otherwise an error
	//
	// Example:
	//	// Atomically get and remove a value
	//	value, err := cache.GetAndDelete(ctx, "queue:item:123")
	GetAndDelete(ctx context.Context, key string) ([]byte, error)

	// Delete removes the item associated with the given key from the cache.
	//
	// If the key does not exist, this operation performs no action and returns nil.
	// The operation is safe for concurrent use by multiple goroutines.
	//
	// Parameters:
	//   - ctx: Context for cancellation and timeouts
	//   - key: The key to remove from the cache
	//
	// Returns:
	//   - error: nil on success, otherwise an error describing the failure
	//
	// Example:
	//	// Remove a specific key
	//	err := cache.Delete(ctx, "user:123")
	Delete(ctx context.Context, key string) error

	// Cleanup removes all expired items from the cache.
	//
	// This operation scans the entire cache and removes any items that have expired.
	// The operation is safe for concurrent use by multiple goroutines.
	// Note that some cache implementations (like Redis) handle expiration automatically
	// and may not require explicit cleanup.
	//
	// Parameters:
	//   - ctx: Context for cancellation and timeouts
	//
	// Returns:
	//   - error: nil on success, otherwise an error describing the failure
	//
	// Example:
	//	// Periodically clean up expired items
	//	err := cache.Cleanup(ctx)
	Cleanup(ctx context.Context) error

	// Drain returns a map of all non-expired items in the cache and clears the cache.
	//
	// The returned map is a snapshot of the cache at the time of the call.
	// After this operation, the cache will be empty. This is useful for cache migration,
	// backup, or when shutting down an application.
	// The operation is safe for concurrent use by multiple goroutines.
	//
	// Parameters:
	//   - ctx: Context for cancellation and timeouts
	//
	// Returns:
	//   - map[string][]byte: A map containing all non-expired key-value pairs
	//   - error: nil on success, otherwise an error describing the failure
	//
	// Example:
	//	// Get all items and clear the cache
	//	items, err := cache.Drain(ctx)
	//	for key, value := range items {
	//	    log.Printf("Drained: %s = %s", key, string(value))
	//	}
	Drain(ctx context.Context) (map[string][]byte, error)

	// Close releases any resources held by the cache.
	//
	// This should be called when the cache is no longer needed. For some implementations
	// (like Redis), this may close network connections. The operation is safe for
	// concurrent use by multiple goroutines.
	//
	// Returns:
	//   - error: nil on success, otherwise an error describing the failure
	//
	// Example:
	//	// Properly close the cache when done
	//	defer cache.Close()
	Close() error
}

Cache defines the interface for cache implementations.

All cache operations are context-aware and support cancellation and timeouts. Implementations must be safe for concurrent use by multiple goroutines.

type GetOption added in v1.31.0

type GetOption func(*getOptions)

GetOption configures the behavior of Get operations.

GetOptions allow you to perform additional operations during a Get call, such as updating the item's TTL, setting a new expiration time, or deleting the item after retrieval.

func AndDefaultTTL added in v1.31.0

func AndDefaultTTL() GetOption

AndDefaultTTL resets the item's TTL to the cache's default TTL during a Get operation.

This option is useful when you want to restore an item to the default expiration policy of the cache, regardless of its current TTL.

Returns:

  • GetOption: An option that resets the TTL to the cache default when passed to Get

Example:

// Get a value and reset its TTL to the cache default
value, err := cache.Get(ctx, "session:abc", cache.AndDefaultTTL())

func AndDelete added in v1.31.0

func AndDelete() GetOption

AndDelete deletes the item from the cache during a Get operation.

This option provides an atomic "get and delete" operation, which is useful for implementing queue-like behavior or ensuring that an item is only processed once. This is equivalent to calling GetAndDelete.

Returns:

  • GetOption: An option that deletes the item when passed to Get

Example:

// Get a value and remove it from the cache
value, err := cache.Get(ctx, "queue:item:123", cache.AndDelete())

func AndSetTTL added in v1.31.0

func AndSetTTL(ttl time.Duration) GetOption

AndSetTTL sets a new TTL for the item during a Get operation.

This option is useful for extending the lifetime of an item when it's accessed, implementing a "touch" behavior where frequently accessed items remain in cache longer.

Parameters:

  • ttl: The new TTL duration to set for the item

Returns:

  • GetOption: An option that sets the TTL when passed to Get

Example:

// Get a value and extend its TTL to 30 minutes from now
value, err := cache.Get(ctx, "session:abc", cache.AndSetTTL(30*time.Minute))

func AndSetValidUntil added in v1.31.0

func AndSetValidUntil(validUntil time.Time) GetOption

AndSetValidUntil sets a new exact expiration time for the item during a Get operation.

This option allows you to set a precise expiration time for an item when it's accessed, which can be useful for implementing time-based access patterns.

Parameters:

  • validUntil: The new exact expiration time for the item

Returns:

  • GetOption: An option that sets the expiration time when passed to Get

Example:

// Get a value and set it to expire at midnight
midnight := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Local)
midnight = midnight.Add(24 * time.Hour) // Next midnight
value, err := cache.Get(ctx, "daily:report", cache.AndSetValidUntil(midnight))

func AndUpdateTTL added in v1.31.0

func AndUpdateTTL(ttl time.Duration) GetOption

AndUpdateTTL extends the current TTL of an item by the given duration.

Unlike AndSetTTL which sets an absolute TTL from the current time, AndUpdateTTL adds the specified duration to the item's existing TTL. This is useful for incrementally extending the lifetime of an item.

Parameters:

  • ttl: The duration to add to the item's current TTL

Returns:

  • GetOption: An option that extends the TTL when passed to Get

Example:

// Get a value and extend its TTL by 15 minutes
value, err := cache.Get(ctx, "session:abc", cache.AndUpdateTTL(15*time.Minute))

type Item added in v1.35.0

type Item interface {
	// Marshal converts the item to a byte slice for storage in the cache.
	//
	// This method is called when the item is stored in the cache.
	// Common implementations include JSON, protobuf, or other serialization formats.
	//
	// Returns:
	//   - []byte: The serialized representation of the item
	//   - error: An error if serialization fails
	Marshal() ([]byte, error)

	// Unmarshal populates the item from a byte slice retrieved from the cache.
	//
	// This method is called when the item is retrieved from the cache.
	// It should restore the item's state from the serialized data.
	//
	// Parameters:
	//   - data: The serialized representation of the item
	//
	// Returns:
	//   - error: An error if deserialization fails
	Unmarshal(data []byte) error
}

Item defines the interface that types must implement to be used with Typed cache.

Types that implement this interface can be automatically serialized and deserialized when stored in or retrieved from the cache. This allows for type-safe caching of complex data structures.

Example implementation:

type User struct {
    ID   string
    Name string
}

func (u *User) Marshal() ([]byte, error) {
    return json.Marshal(u)
}

func (u *User) Unmarshal(data []byte) error {
    return json.Unmarshal(data, u)
}

type MemoryCache added in v1.34.3

type MemoryCache struct {
	// contains filtered or unexported fields
}

MemoryCache implements an in-memory cache with TTL support.

This implementation stores all data in a Go map protected by a read-write mutex, making it safe for concurrent access by multiple goroutines. Items are automatically checked for expiration on access, but expired items remain in memory until explicitly removed by a Cleanup operation or overwritten.

The memory cache is suitable for:

  • Single-process applications
  • Caching small to medium amounts of data
  • Scenarios where low latency is critical
  • Temporary caching that doesn't need persistence

For distributed caching or persistence, consider using the Redis implementation instead.

func NewMemory

func NewMemory(ttl time.Duration) *MemoryCache

NewMemory creates a new in-memory cache with the specified default TTL.

The TTL parameter sets the default time-to-live for items stored in the cache. A TTL of zero means items do not expire by default, but individual items can still have their own TTL set via options.

Parameters:

  • ttl: The default TTL for cache items. Zero means no expiration.

Returns:

  • *MemoryCache: A new in-memory cache instance

Example:

// Create a cache with 1 hour default TTL
cache := cache.NewMemory(time.Hour)

// Create a cache with no expiration
cache := cache.NewMemory(0)

func (*MemoryCache) Cleanup added in v1.34.3

func (m *MemoryCache) Cleanup(_ context.Context) error

Cleanup removes all expired items from the memory cache.

This method scans through all items in the cache and removes any that have expired. The operation is performed atomically with a write lock to ensure consistency. Note that this is a manual cleanup operation - expired items are also checked during normal Get operations, but this method explicitly removes them from memory.

Parameters:

  • ctx: Context for cancellation and timeouts (currently unused but kept for interface compatibility)

Returns:

  • error: Always nil for memory cache

Example:

// Periodically clean up expired items
err := cache.Cleanup(ctx)

func (*MemoryCache) Close added in v1.34.3

func (m *MemoryCache) Close() error

Close releases any resources held by the memory cache.

For the memory cache implementation, this is a no-op since there are no external resources to clean up. The method is included for interface compatibility.

Returns:

  • error: Always nil for memory cache

Example:

// Properly close the cache when done
defer cache.Close()

func (*MemoryCache) Delete added in v1.34.3

func (m *MemoryCache) Delete(_ context.Context, key string) error

Delete removes the item associated with the given key from the memory cache.

If the key does not exist, this operation performs no action and returns nil. The operation is safe for concurrent use by multiple goroutines.

Parameters:

  • ctx: Context for cancellation and timeouts (currently unused but kept for interface compatibility)
  • key: The key to remove from the cache

Returns:

  • error: Always nil for memory cache

Example:

// Remove a specific key
err := cache.Delete(ctx, "user:123")

func (*MemoryCache) Drain added in v1.34.3

func (m *MemoryCache) Drain(_ context.Context) (map[string][]byte, error)

Drain returns a map of all non-expired items in the memory cache and clears the cache.

The returned map is a snapshot of the cache at the time of the call. After this operation, the cache will be empty. This is useful for cache migration, backup, or when shutting down an application. The operation is performed atomically with a write lock to ensure consistency.

Parameters:

  • ctx: Context for cancellation and timeouts (currently unused but kept for interface compatibility)

Returns:

  • map[string][]byte: A map containing all non-expired key-value pairs
  • error: Always nil for memory cache

Example:

// Get all items and clear the cache
items, err := cache.Drain(ctx)
for key, value := range items {
    log.Printf("Drained: %s = %s", key, string(value))
}

func (*MemoryCache) Get added in v1.34.3

func (m *MemoryCache) Get(_ context.Context, key string, opts ...GetOption) ([]byte, error)

Get retrieves the value for the given key from the memory cache.

The behavior depends on the key's existence and expiration state:

  • If the key exists and has not expired, returns the value and nil error
  • If the key does not exist, returns nil and ErrKeyNotFound
  • If the key exists but has expired, returns nil and ErrKeyExpired

GetOptions can be used to modify the behavior, such as updating TTL, deleting the key after retrieval, or setting a new expiration time.

Parameters:

  • ctx: Context for cancellation and timeouts (currently unused but kept for interface compatibility)
  • key: The key to retrieve
  • opts: Optional operations to perform during retrieval (e.g., AndDelete, AndSetTTL)

Returns:

  • []byte: The cached value if found and not expired
  • error: nil on success, ErrKeyNotFound if key doesn't exist, ErrKeyExpired if key exists but has expired, otherwise an error

Example:

// Simple get
value, err := cache.Get(ctx, "user:123")

// Get and extend TTL by 30 minutes
value, err := cache.Get(ctx, "session:abc", cache.AndUpdateTTL(30*time.Minute))

// Get and delete atomically
value, err := cache.Get(ctx, "temp:xyz", cache.AndDelete())

func (*MemoryCache) GetAndDelete added in v1.34.3

func (m *MemoryCache) GetAndDelete(ctx context.Context, key string) ([]byte, error)

GetAndDelete retrieves the value for the given key and atomically deletes it from the memory cache.

This is equivalent to calling Get with the AndDelete option, but provides a more convenient API for the common pattern of reading and removing a value in one operation.

Parameters:

  • ctx: Context for cancellation and timeouts
  • key: The key to retrieve and delete

Returns:

  • []byte: The cached value if found and not expired
  • error: nil on success, ErrKeyNotFound if key doesn't exist, ErrKeyExpired if key exists but has expired, otherwise an error

Example:

// Atomically get and remove a value
value, err := cache.GetAndDelete(ctx, "queue:item:123")

func (*MemoryCache) Set added in v1.34.3

func (m *MemoryCache) Set(_ context.Context, key string, value []byte, opts ...Option) error

Set stores the value for the given key in the memory cache, overwriting any existing value.

The value will be stored with the default TTL configured for the cache, unless overridden by options. If the key already exists, its value and TTL will be updated.

Parameters:

  • ctx: Context for cancellation and timeouts (currently unused but kept for interface compatibility)
  • key: The key to store the value under
  • value: The value to store as a byte slice
  • opts: Optional configuration for this specific item (e.g., custom TTL)

Returns:

  • error: Always nil for memory cache

Example:

// Set with default TTL
err := cache.Set(ctx, "user:123", []byte("user data"))

// Set with custom TTL
err := cache.Set(ctx, "session:abc", []byte("session data"), cache.WithTTL(30*time.Minute))

func (*MemoryCache) SetOrFail added in v1.34.3

func (m *MemoryCache) SetOrFail(_ context.Context, key string, value []byte, opts ...Option) error

SetOrFail stores the value for the given key only if the key does not already exist.

This is an atomic operation that prevents race conditions when multiple goroutines might try to set the same key simultaneously. If the key exists but has expired, it will be overwritten.

Parameters:

  • ctx: Context for cancellation and timeouts (currently unused but kept for interface compatibility)
  • key: The key to store the value under
  • value: The value to store as a byte slice
  • opts: Optional configuration for this specific item (e.g., custom TTL)

Returns:

  • error: nil on success, ErrKeyExists if the key already exists and is not expired, otherwise an error

Example:

// Try to set a value only if key doesn't exist
err := cache.SetOrFail(ctx, "lock:resource", []byte("locked"))
if errors.Is(err, cache.ErrKeyExists) {
    // Key already exists, handle conflict
}

type Option

type Option func(*options)

Option configures per-item cache behavior (e.g., expiry).

Options are used with Set and SetOrFail operations to customize how individual cache items are stored, including their expiration time and TTL.

func WithTTL

func WithTTL(ttl time.Duration) Option

WithTTL sets the TTL (time to live) for an item.

The item will expire after the given duration from the time of insertion. A TTL of zero means the item will not expire. A negative TTL means the item expires immediately.

Parameters:

  • ttl: The duration after which the item will expire

Returns:

  • Option: An option that sets the TTL when passed to Set or SetOrFail

Example:

// Set a value that expires in 30 minutes
err := cache.Set(ctx, "session:abc", []byte("data"), cache.WithTTL(30*time.Minute))

func WithValidUntil

func WithValidUntil(validUntil time.Time) Option

WithValidUntil sets the exact expiration time for an item.

The item will expire at the given time, regardless of when it was inserted. This is useful when you need precise control over when an item expires, such as at midnight or the end of a billing period.

Parameters:

  • validUntil: The exact time when the item should expire

Returns:

  • Option: An option that sets the expiration time when passed to Set or SetOrFail

Example:

// Set a value that expires at midnight
midnight := time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 0, 0, 0, 0, time.Local)
midnight = midnight.Add(24 * time.Hour) // Next midnight
err := cache.Set(ctx, "daily:report", []byte("data"), cache.WithValidUntil(midnight))

type RedisCache added in v1.34.3

type RedisCache struct {
	// contains filtered or unexported fields
}

RedisCache implements the Cache interface using Redis as the backend.

This implementation stores all data in a Redis hash, with each cache item being a field in the hash. It uses Redis's built-in TTL functionality for expiration and Lua scripts for atomic operations.

The Redis cache is suitable for:

  • Distributed applications where multiple processes need access to the same cache
  • Caching large amounts of data that don't fit in memory
  • Scenarios where cache persistence is required
  • High-availability caching with Redis clustering

For single-process applications or when low latency is critical, consider using the in-memory implementation instead.

func NewRedis

func NewRedis(config RedisConfig) (*RedisCache, error)

NewRedis creates a new Redis cache with the specified configuration.

This function validates the configuration and creates a Redis client if one is not provided. The key used for storing cache items in Redis is constructed from the prefix and a constant suffix.

Parameters:

  • config: Configuration for the Redis cache

Returns:

  • *redisCache: A new Redis cache instance
  • error: An error if the configuration is invalid or the Redis client cannot be created

Example:

// Create a Redis cache with a new client
config := cache.RedisConfig{
    URL:    "redis://localhost:6379",
    Prefix: "myapp:",
    TTL:    time.Hour,
}
redisCache, err := cache.NewRedis(config)
if err != nil {
    log.Fatal(err)
}
defer redisCache.Close()

// Create a Redis cache with an existing client
client := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
config := cache.RedisConfig{
    Client: client,
    Prefix: "myapp:",
    TTL:    time.Hour,
}
redisCache, err := cache.NewRedis(config)

func (*RedisCache) Cleanup added in v1.34.3

func (r *RedisCache) Cleanup(_ context.Context) error

Cleanup removes all expired items from the Redis cache.

For Redis cache implementation, this is a no-op because Redis handles expiration automatically. Expired items are automatically removed by Redis based on their TTL settings.

Parameters:

  • ctx: Context for cancellation and timeouts (currently unused but kept for interface compatibility)

Returns:

  • error: Always nil for Redis cache

Example:

// No-op for Redis cache, but included for interface compatibility
err := redisCache.Cleanup(ctx)

func (*RedisCache) Close added in v1.34.3

func (r *RedisCache) Close() error

Close releases any resources held by the Redis cache.

If the Redis cache was created with a client (rather than using an existing client), this method will close the Redis client connection. If an existing client was provided, this method is a no-op to avoid closing a client that might be used elsewhere.

Returns:

  • error: nil on success, or an error if closing the Redis client fails

Example:

// Properly close the cache when done
defer redisCache.Close()

func (*RedisCache) Delete added in v1.34.3

func (r *RedisCache) Delete(ctx context.Context, key string) error

Delete removes the item associated with the given key from the Redis cache.

If the key does not exist, this operation performs no action and returns nil. The operation uses Redis's HDEL command to remove the field from the hash.

Parameters:

  • ctx: Context for cancellation and timeouts
  • key: The key to remove from the cache

Returns:

  • error: nil on success, or an error if the Redis operation fails

Example:

// Remove a specific key
err := redisCache.Delete(ctx, "user:123")

func (*RedisCache) Drain added in v1.34.3

func (r *RedisCache) Drain(ctx context.Context) (map[string][]byte, error)

Drain returns a map of all non-expired items in the Redis cache and clears the cache.

This operation uses a Lua script to atomically get all fields from the hash and then delete the entire hash. This ensures that the operation is atomic and no items are lost between the get and delete operations.

Parameters:

  • ctx: Context for cancellation and timeouts

Returns:

  • map[string][]byte: A map containing all non-expired key-value pairs
  • error: nil on success, or an error if the Redis operation fails

Example:

// Get all items and clear the cache
items, err := redisCache.Drain(ctx)
for key, value := range items {
    log.Printf("Drained: %s = %s", key, string(value))
}

func (*RedisCache) Get added in v1.34.3

func (r *RedisCache) Get(ctx context.Context, key string, opts ...GetOption) ([]byte, error)

Get retrieves the value for the given key from the Redis cache.

The behavior depends on the key's existence and expiration state:

  • If the key exists and has not expired, returns the value and nil error
  • If the key does not exist, returns nil and ErrKeyNotFound
  • If the key exists but has expired, returns nil and ErrKeyExpired

GetOptions can be used to modify the behavior, such as updating TTL, deleting the key after retrieval, or setting a new expiration time. When GetOptions are provided, a Lua script is used for atomic operations.

Parameters:

  • ctx: Context for cancellation and timeouts
  • key: The key to retrieve
  • opts: Optional operations to perform during retrieval (e.g., AndDelete, AndSetTTL)

Returns:

  • []byte: The cached value if found and not expired
  • error: nil on success, ErrKeyNotFound if key doesn't exist, ErrKeyExpired if key exists but has expired, otherwise an error

Example:

// Simple get
value, err := redisCache.Get(ctx, "user:123")

// Get and extend TTL by 30 minutes
value, err := redisCache.Get(ctx, "session:abc", cache.AndUpdateTTL(30*time.Minute))

// Get and delete atomically
value, err := redisCache.Get(ctx, "temp:xyz", cache.AndDelete())

func (*RedisCache) GetAndDelete added in v1.34.3

func (r *RedisCache) GetAndDelete(ctx context.Context, key string) ([]byte, error)

GetAndDelete retrieves the value for the given key and atomically deletes it from the Redis cache.

This is equivalent to calling Get with the AndDelete option, but provides a more convenient API for the common pattern of reading and removing a value in one operation.

Parameters:

  • ctx: Context for cancellation and timeouts
  • key: The key to retrieve and delete

Returns:

  • []byte: The cached value if found and not expired
  • error: nil on success, ErrKeyNotFound if key doesn't exist, ErrKeyExpired if key exists but has expired, otherwise an error

Example:

// Atomically get and remove a value
value, err := redisCache.GetAndDelete(ctx, "queue:item:123")

func (*RedisCache) Set added in v1.34.3

func (r *RedisCache) Set(ctx context.Context, key string, value []byte, opts ...Option) error

Set stores the value for the given key in the Redis cache, overwriting any existing value.

The value will be stored with the default TTL configured for the cache, unless overridden by options. If the key already exists, its value and TTL will be updated. This method uses Redis pipelining to ensure that the set and TTL operations are atomic.

Parameters:

  • ctx: Context for cancellation and timeouts
  • key: The key to store the value under
  • value: The value to store as a byte slice
  • opts: Optional configuration for this specific item (e.g., custom TTL)

Returns:

  • error: nil on success, or an error if the Redis operation fails

Example:

// Set with default TTL
err := redisCache.Set(ctx, "user:123", []byte("user data"))

// Set with custom TTL
err := redisCache.Set(ctx, "session:abc", []byte("session data"), cache.WithTTL(30*time.Minute))

func (*RedisCache) SetOrFail added in v1.34.3

func (r *RedisCache) SetOrFail(ctx context.Context, key string, value []byte, opts ...Option) error

SetOrFail stores the value for the given key only if the key does not already exist.

This is an atomic operation that prevents race conditions when multiple goroutines might try to set the same key simultaneously. It uses Redis's HSetNX command which only sets the field if it doesn't already exist.

Parameters:

  • ctx: Context for cancellation and timeouts
  • key: The key to store the value under
  • value: The value to store as a byte slice
  • opts: Optional configuration for this specific item (e.g., custom TTL)

Returns:

  • error: nil on success, ErrKeyExists if the key already exists and is not expired, otherwise an error

Example:

// Try to set a value only if key doesn't exist
err := redisCache.SetOrFail(ctx, "lock:resource", []byte("locked"))
if errors.Is(err, cache.ErrKeyExists) {
    // Key already exists, handle conflict
}

type RedisConfig added in v1.32.0

type RedisConfig struct {
	// Client is the Redis client to use.
	// If nil, a client is created from the URL.
	Client *redis.Client

	// URL is the Redis URL to use.
	// If empty, the Redis client is not created.
	URL string

	// Prefix is the prefix to use for all keys in the Redis cache.
	// This helps avoid key collisions when multiple applications use the same Redis instance.
	Prefix string

	// TTL is the time-to-live for all cache entries.
	// This is the default TTL used when no explicit TTL is provided.
	TTL time.Duration
}

RedisConfig configures the Redis cache backend.

This struct provides configuration options for creating a Redis-based cache implementation. You can either provide an existing Redis client or let the cache create one from a URL.

type Typed added in v1.35.0

type Typed[T Item] struct {
	// contains filtered or unexported fields
}

Typed provides a type-safe wrapper around a Cache implementation.

This generic wrapper allows caching of specific types that implement the Item interface, providing compile-time type safety and eliminating the need for manual type assertions when working with cached values.

The Typed wrapper handles serialization and deserialization automatically, making it easy to cache complex data structures while maintaining type safety.

Example usage:

// Define a type that implements Item
type User struct {
    ID   string
    Name string
}

func (u *User) Marshal() ([]byte, error) {
    return json.Marshal(u)
}

func (u *User) Unmarshal(data []byte) error {
    return json.Unmarshal(data, u)
}

// Create a typed cache
storage := cache.NewMemory(time.Hour)
userCache := cache.NewTyped[*User](storage)

// Set a typed value
user := &User{ID: "123", Name: "Alice"}
err := userCache.Set(ctx, "user:123", user)

// Get a typed value
retrieved, err := userCache.Get(ctx, "user:123")
// retrieved is of type *User, no type assertion needed

func NewTyped added in v1.35.0

func NewTyped[T Item](storage Cache) *Typed[T]

NewTyped creates a new typed cache wrapper around the provided storage.

The typed cache uses the underlying storage for all operations but adds automatic serialization and deserialization of values that implement the Item interface.

Parameters:

  • storage: The underlying cache implementation to wrap

Returns:

  • *Typed[T]: A new typed cache wrapper

Example:

// Create a typed cache with in-memory storage
storage := cache.NewMemory(time.Hour)
userCache := cache.NewTyped[*User](storage)

// Create a typed cache with Redis storage
config := cache.RedisConfig{URL: "redis://localhost:6379"}
redisCache, err := cache.NewRedis(config)
if err != nil {
    log.Fatal(err)
}
defer redisCache.Close()
userCache := cache.NewTyped[*User](redisCache)

func (*Typed[T]) Cleanup added in v1.35.0

func (c *Typed[T]) Cleanup(ctx context.Context) error

Cleanup removes all expired items from the cache.

This operation scans the entire cache and removes any items that have expired. The operation is safe for concurrent use by multiple goroutines. Note that some cache implementations (like Redis) handle expiration automatically and may not require explicit cleanup.

Parameters:

  • ctx: Context for cancellation and timeouts

Returns:

  • error: nil on success, or an error if the cache operation fails

Example:

// Periodically clean up expired items
err := userCache.Cleanup(ctx)

func (*Typed[T]) Close added in v1.35.0

func (c *Typed[T]) Close() error

Close releases any resources held by the cache.

This should be called when the cache is no longer needed. For some implementations (like Redis), this may close network connections. The operation is safe for concurrent use by multiple goroutines.

Returns:

  • error: nil on success, or an error if the cache operation fails

Example:

// Properly close the cache when done
defer userCache.Close()

func (*Typed[T]) Delete added in v1.35.0

func (c *Typed[T]) Delete(ctx context.Context, key string) error

Delete removes the item associated with the given key from the cache.

If the key does not exist, this operation performs no action and returns nil. The operation is safe for concurrent use by multiple goroutines.

Parameters:

  • ctx: Context for cancellation and timeouts
  • key: The key to remove from the cache

Returns:

  • error: nil on success, or an error if the cache operation fails

Example:

// Remove a specific key
err := userCache.Delete(ctx, "user:123")

func (*Typed[T]) Drain added in v1.35.0

func (c *Typed[T]) Drain(ctx context.Context) (map[string]T, error)

Drain returns a map of all non-expired typed items in the cache and clears the cache.

The returned map is a snapshot of the cache at the time of the call. After this operation, the cache will be empty. This is useful for cache migration, backup, or when shutting down an application. The operation is safe for concurrent use by multiple goroutines.

Parameters:

  • ctx: Context for cancellation and timeouts

Returns:

  • map[string]T: A map containing all non-expired key-typed value pairs
  • error: nil on success, or an error if the cache operation fails

Example:

// Get all items and clear the cache
users, err := userCache.Drain(ctx)
for key, user := range users {
    log.Printf("Drained: %s = %+v", key, user)
}

func (*Typed[T]) Get added in v1.35.0

func (c *Typed[T]) Get(ctx context.Context, key string, opts ...GetOption) (T, error)

Get retrieves the typed value for the given key from the cache.

The behavior depends on the key's existence and expiration state:

  • If the key exists and has not expired, returns the typed value and nil error
  • If the key does not exist, returns a zero value and ErrKeyNotFound
  • If the key exists but has expired, returns a zero value and ErrKeyExpired

The retrieved bytes are automatically unmarshaled to the correct type using the Unmarshal method. GetOptions can be used to modify the behavior, such as updating TTL, deleting the key after retrieval, or setting a new expiration time.

Parameters:

  • ctx: Context for cancellation and timeouts
  • key: The key to retrieve
  • opts: Optional operations to perform during retrieval (e.g., AndDelete, AndSetTTL)

Returns:

  • T: The cached typed value if found and not expired, otherwise a zero value
  • error: nil on success, ErrKeyNotFound if key doesn't exist, ErrKeyExpired if key exists but has expired, otherwise an error

Example:

// Simple get
user, err := userCache.Get(ctx, "user:123")

// Get and extend TTL by 30 minutes
user, err := userCache.Get(ctx, "session:abc", cache.AndUpdateTTL(30*time.Minute))

// Get and delete atomically
user, err := userCache.Get(ctx, "temp:xyz", cache.AndDelete())

func (*Typed[T]) GetAndDelete added in v1.35.0

func (c *Typed[T]) GetAndDelete(ctx context.Context, key string) (T, error)

GetAndDelete retrieves the typed value for the given key and atomically deletes it from the cache.

This is equivalent to calling Get with the AndDelete option, but provides a more convenient API for the common pattern of reading and removing a value in one operation.

Parameters:

  • ctx: Context for cancellation and timeouts
  • key: The key to retrieve and delete

Returns:

  • T: The cached typed value if found and not expired, otherwise a zero value
  • error: nil on success, ErrKeyNotFound if key doesn't exist, ErrKeyExpired if key exists but has expired, otherwise an error

Example:

// Atomically get and remove a value
user, err := userCache.GetAndDelete(ctx, "queue:item:123")

func (*Typed[T]) Set added in v1.35.0

func (c *Typed[T]) Set(ctx context.Context, key string, value T, opts ...Option) error

Set stores the typed value for the given key in the cache, overwriting any existing value.

The value will be automatically marshaled to bytes before storage using its Marshal method. The value will be stored with the default TTL configured for the cache implementation, unless overridden by options.

Parameters:

  • ctx: Context for cancellation and timeouts
  • key: The key to store the value under
  • value: The typed value to store
  • opts: Optional configuration for this specific item (e.g., custom TTL)

Returns:

  • error: nil on success, or an error if marshaling fails or the cache operation fails

Example:

// Set with default TTL
user := &User{ID: "123", Name: "Alice"}
err := userCache.Set(ctx, "user:123", user)

// Set with custom TTL
err := userCache.Set(ctx, "session:abc", user, cache.WithTTL(30*time.Minute))

func (*Typed[T]) SetOrFail added in v1.35.0

func (c *Typed[T]) SetOrFail(ctx context.Context, key string, value T, opts ...Option) error

SetOrFail stores the typed value for the given key only if the key does not already exist.

This is an atomic operation that prevents race conditions when multiple goroutines might try to set the same key simultaneously. The value will be automatically marshaled to bytes before storage using its Marshal method.

Parameters:

  • ctx: Context for cancellation and timeouts
  • key: The key to store the value under
  • value: The typed value to store
  • opts: Optional configuration for this specific item (e.g., custom TTL)

Returns:

  • error: nil on success, ErrKeyExists if the key already exists and is not expired, otherwise an error if marshaling fails or the cache operation fails

Example:

// Try to set a value only if key doesn't exist
user := &User{ID: "123", Name: "Alice"}
err := userCache.SetOrFail(ctx, "user:123", user)
if errors.Is(err, cache.ErrKeyExists) {
    // Key already exists, handle conflict
}

Jump to

Keyboard shortcuts

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