cache

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Jun 28, 2025 License: MIT Imports: 8 Imported by: 1

README

Cache Module

Go Reference

The Cache Module provides caching functionality for Modular applications. It offers different cache backend options including in-memory and Redis (placeholder implementation).

Features

  • Multiple cache engine options (memory, Redis)
  • Support for time-to-live (TTL) expirations
  • Automatic cache cleanup for expired items
  • Basic cache operations (get, set, delete)
  • Bulk operations (getMulti, setMulti, deleteMulti)

Installation

import (
    "github.com/GoCodeAlone/modular"
    "github.com/GoCodeAlone/modular/modules/cache"
)

// Register the cache module with your Modular application
app.RegisterModule(cache.NewModule())

Configuration

The cache module can be configured using the following options:

cache:
  engine: memory            # Cache engine to use: "memory" or "redis"
  defaultTTL: 300           # Default TTL in seconds if not specified (300s = 5 minutes)
  cleanupInterval: 60       # How often to clean up expired items (60s = 1 minute)
  maxItems: 10000           # Maximum items to store in memory cache
  redisURL: ""              # Redis connection URL (for Redis engine)
  redisPassword: ""         # Redis password (for Redis engine)
  redisDB: 0                # Redis database number (for Redis engine)
  connectionMaxAge: 60      # Maximum age of connections in seconds

Usage

Accessing the Cache Service
// In your module's Init function
func (m *MyModule) Init(app modular.Application) error {
    var cacheService *cache.CacheModule
    err := app.GetService("cache.provider", &cacheService)
    if err != nil {
        return fmt.Errorf("failed to get cache service: %w", err)
    }
    
    // Now you can use the cache service
    m.cache = cacheService
    return nil
}
Using Interface-Based Service Matching
// Define the service dependency
func (m *MyModule) RequiresServices() []modular.ServiceDependency {
    return []modular.ServiceDependency{
        {
            Name:               "cache",
            Required:           true,
            MatchByInterface:   true,
            SatisfiesInterface: reflect.TypeOf((*cache.CacheEngine)(nil)).Elem(),
        },
    }
}

// Access the service in your constructor
func (m *MyModule) Constructor() modular.ModuleConstructor {
    return func(app modular.Application, services map[string]any) (modular.Module, error) {
        cacheService := services["cache"].(cache.CacheEngine)
        return &MyModule{cache: cacheService}, nil
    }
}
Basic Operations
// Set a value in the cache with a TTL
err := cacheService.Set(ctx, "user:123", userData, 5*time.Minute)
if err != nil {
    // Handle error
}

// Get a value from the cache
value, found := cacheService.Get(ctx, "user:123")
if !found {
    // Cache miss, fetch from primary source
} else {
    userData = value.(UserData)
}

// Delete a value from the cache
err := cacheService.Delete(ctx, "user:123")
if err != nil {
    // Handle error
}
Bulk Operations
// Get multiple values
keys := []string{"user:123", "user:456", "user:789"}
results, err := cacheService.GetMulti(ctx, keys)
if err != nil {
    // Handle error
}

// Set multiple values
items := map[string]interface{}{
    "user:123": userData1,
    "user:456": userData2,
}
err := cacheService.SetMulti(ctx, items, 10*time.Minute)
if err != nil {
    // Handle error
}

// Delete multiple values
err := cacheService.DeleteMulti(ctx, keys)
if err != nil {
    // Handle error
}

Implementation Notes

  • The in-memory cache uses Go's built-in concurrency primitives for thread safety
  • Redis implementation is provided as a placeholder and would require a Redis client implementation
  • The cache automatically cleans up expired items at configurable intervals

Testing

The cache module includes comprehensive tests for the memory cache implementation.

Documentation

Overview

Package cache provides a flexible caching module for the modular framework.

This module supports multiple cache backends including in-memory and Redis, with configurable TTL, cleanup intervals, and connection management. It provides a unified interface for caching operations across different storage engines.

Supported Cache Engines

The cache module supports the following engines:

  • "memory": In-memory cache with LRU eviction and TTL support
  • "redis": Redis-based cache with connection pooling and persistence

Configuration

The module can be configured through the CacheConfig structure:

config := &CacheConfig{
    Engine:           "memory",     // or "redis"
    DefaultTTL:       300,          // 5 minutes default TTL
    CleanupInterval:  60,           // cleanup every minute
    MaxItems:         10000,        // max items for memory cache
    RedisURL:         "redis://localhost:6379", // for Redis engine
    RedisPassword:    "",           // Redis password if required
    RedisDB:          0,            // Redis database number
    ConnectionMaxAge: 60,           // connection max age in seconds
}

Service Registration

The module registers itself as a service that can be injected into other modules:

// Get the cache service
cacheService := app.GetService("cache.provider").(*CacheModule)

// Use the cache
err := cacheService.Set(ctx, "key", "value", time.Minute*5)
value, found := cacheService.Get(ctx, "key")

Usage Examples

Basic caching operations:

// Set a value with default TTL
err := cache.Set(ctx, "user:123", userData, 0)

// Set a value with custom TTL
err := cache.Set(ctx, "session:abc", sessionData, time.Hour)

// Get a value
value, found := cache.Get(ctx, "user:123")
if found {
    user := value.(UserData)
    // use user data
}

// Batch operations
items := map[string]interface{}{
    "key1": "value1",
    "key2": "value2",
}
err := cache.SetMulti(ctx, items, time.Minute*10)

results, err := cache.GetMulti(ctx, []string{"key1", "key2"})

Index

Constants

View Source
const ModuleName = "cache"

ModuleName is the unique identifier for the cache module.

View Source
const ServiceName = "cache.provider"

ServiceName is the name of the service provided by this module. Other modules can use this name to request the cache service through dependency injection.

Variables

View Source
var (
	// ErrCacheFull is returned when the memory cache is full and cannot store new items
	ErrCacheFull = errors.New("cache is full")

	// ErrInvalidKey is returned when the key is invalid
	ErrInvalidKey = errors.New("invalid cache key")

	// ErrInvalidValue is returned when the value cannot be stored in the cache
	ErrInvalidValue = errors.New("invalid cache value")

	// ErrNotConnected is returned when an operation is attempted on a cache that is not connected
	ErrNotConnected = errors.New("cache not connected")
)

Error definitions

Functions

func NewModule

func NewModule() modular.Module

NewModule creates a new instance of the cache module. This is the primary constructor for the cache module and should be used when registering the module with the application.

Example:

app.RegisterModule(cache.NewModule())

Types

type CacheConfig

type CacheConfig struct {
	// Engine specifies the cache engine to use.
	// Supported values: "memory", "redis"
	// Default: "memory"
	Engine string `json:"engine" yaml:"engine" env:"ENGINE" validate:"oneof=memory redis"`

	// DefaultTTL is the default time-to-live for cache entries in seconds.
	// Used when no explicit TTL is provided in cache operations.
	// Must be at least 1 second.
	DefaultTTL int `json:"defaultTTL" yaml:"defaultTTL" env:"DEFAULT_TTL" validate:"min=1"`

	// CleanupInterval is how often to clean up expired items (in seconds).
	// Only applicable to memory cache engine.
	// Must be at least 1 second.
	CleanupInterval int `json:"cleanupInterval" yaml:"cleanupInterval" env:"CLEANUP_INTERVAL" validate:"min=1"`

	// MaxItems is the maximum number of items to store in memory cache.
	// When this limit is reached, least recently used items are evicted.
	// Only applicable to memory cache engine.
	// Must be at least 1.
	MaxItems int `json:"maxItems" yaml:"maxItems" env:"MAX_ITEMS" validate:"min=1"`

	// RedisURL is the connection URL for Redis server.
	// Format: redis://[username:password@]host:port[/database]
	// Only required when using Redis engine.
	// Example: "redis://localhost:6379", "redis://user:pass@localhost:6379/1"
	RedisURL string `json:"redisURL" yaml:"redisURL" env:"REDIS_URL"`

	// RedisPassword is the password for Redis authentication.
	// Optional if Redis server doesn't require authentication.
	RedisPassword string `json:"redisPassword" yaml:"redisPassword" env:"REDIS_PASSWORD"`

	// RedisDB is the Redis database number to use.
	// Redis supports multiple databases (0-15 by default).
	// Must be non-negative.
	RedisDB int `json:"redisDB" yaml:"redisDB" env:"REDIS_DB" validate:"min=0"`

	// ConnectionMaxAge is the maximum age of a connection in seconds.
	// Connections older than this will be closed and recreated.
	// Helps prevent connection staleness in long-running applications.
	// Must be at least 1 second.
	ConnectionMaxAge int `json:"connectionMaxAge" yaml:"connectionMaxAge" env:"CONNECTION_MAX_AGE" validate:"min=1"`
}

CacheConfig defines the configuration for the cache module. This structure contains all the settings needed to configure both memory and Redis cache engines.

Configuration can be provided through JSON, YAML, or environment variables. The struct tags define the mapping for each configuration source and validation rules.

Example JSON configuration:

{
  "engine": "redis",
  "defaultTTL": 600,
  "cleanupInterval": 300,
  "maxItems": 50000,
  "redisURL": "redis://localhost:6379",
  "redisPassword": "mypassword",
  "redisDB": 1
}

Example environment variables:

CACHE_ENGINE=memory
CACHE_DEFAULT_TTL=300
CACHE_MAX_ITEMS=10000

type CacheEngine

type CacheEngine interface {
	// Connect establishes connection to the cache backend.
	// This method is called during module startup and should prepare
	// the engine for cache operations. For memory caches, this might
	// initialize internal data structures. For network-based caches
	// like Redis, this establishes the connection pool.
	//
	// The context can be used to handle connection timeouts.
	Connect(ctx context.Context) error

	// Close closes the connection to the cache backend.
	// This method is called during module shutdown and should cleanup
	// all resources, close network connections, and stop background
	// processes. The method should be idempotent - safe to call multiple times.
	//
	// The context can be used to handle graceful shutdown timeouts.
	Close(ctx context.Context) error

	// Get retrieves an item from the cache.
	// Returns the cached value and a boolean indicating whether the key was found.
	// If the key doesn't exist or has expired, returns (nil, false).
	//
	// The returned value should be the same type that was stored.
	// The context can be used for operation timeouts.
	Get(ctx context.Context, key string) (interface{}, bool)

	// Set stores an item in the cache with a TTL.
	// The value can be any serializable type. The TTL determines how long
	// the item should remain in the cache before expiring.
	//
	// If TTL is 0 or negative, the item should use the default TTL or
	// never expire, depending on the implementation.
	//
	// The context can be used for operation timeouts.
	Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error

	// Delete removes an item from the cache.
	// Should not return an error if the key doesn't exist.
	// Only returns errors for actual operation failures.
	//
	// The context can be used for operation timeouts.
	Delete(ctx context.Context, key string) error

	// Flush removes all items from the cache.
	// This operation should be atomic - either all items are removed
	// or none are. Should be used with caution as it's irreversible.
	//
	// The context can be used for operation timeouts.
	Flush(ctx context.Context) error

	// GetMulti retrieves multiple items from the cache in a single operation.
	// Returns a map containing only the keys that were found.
	// Missing or expired keys are not included in the result.
	//
	// This operation should be more efficient than multiple Get calls
	// for network-based caches.
	//
	// The context can be used for operation timeouts.
	GetMulti(ctx context.Context, keys []string) (map[string]interface{}, error)

	// SetMulti stores multiple items in the cache with a TTL.
	// All items use the same TTL value. This operation should be atomic
	// where possible - either all items are stored or none are.
	//
	// This operation should be more efficient than multiple Set calls
	// for network-based caches.
	//
	// The context can be used for operation timeouts.
	SetMulti(ctx context.Context, items map[string]interface{}, ttl time.Duration) error

	// DeleteMulti removes multiple items from the cache.
	// Should not return an error for keys that don't exist.
	// Only returns errors for actual operation failures.
	//
	// This operation should be more efficient than multiple Delete calls
	// for network-based caches.
	//
	// The context can be used for operation timeouts.
	DeleteMulti(ctx context.Context, keys []string) error
}

CacheEngine defines the interface for cache engine implementations. This interface abstracts the underlying storage mechanism, allowing the cache module to support multiple backends (memory, Redis, etc.) through a common API.

All operations are context-aware to support cancellation and timeouts. Implementations should be thread-safe and handle concurrent access properly.

Cache engines are responsible for:

  • Connection management to the underlying storage
  • Data serialization/deserialization
  • TTL handling and expiration
  • Error handling and recovery

type CacheModule

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

CacheModule provides caching functionality for the modular framework. It supports multiple cache backends (memory and Redis) and provides a unified interface for caching operations including TTL management, batch operations, and automatic cleanup.

The module implements the following interfaces:

  • modular.Module: Basic module lifecycle
  • modular.Configurable: Configuration management
  • modular.ServiceAware: Service dependency management
  • modular.Startable: Startup logic
  • modular.Stoppable: Shutdown logic

Cache operations are thread-safe and support context cancellation.

func (*CacheModule) Constructor

func (m *CacheModule) Constructor() modular.ModuleConstructor

Constructor provides a dependency injection constructor for the module. This method is used by the dependency injection system to create the module instance with any required services.

func (*CacheModule) Delete

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

Delete removes an item from the cache. Returns an error if the deletion fails, but not if the key doesn't exist.

Example:

err := cache.Delete(ctx, "user:123")
if err != nil {
    // handle deletion error
}

func (*CacheModule) DeleteMulti

func (m *CacheModule) DeleteMulti(ctx context.Context, keys []string) error

DeleteMulti removes multiple items from the cache in a single operation. This is more efficient than multiple Delete calls for batch operations. Does not return an error for keys that don't exist.

Example:

keys := []string{"user:123", "user:456", "expired:key"}
err := cache.DeleteMulti(ctx, keys)
if err != nil {
    // handle deletion error
}

func (*CacheModule) Dependencies

func (m *CacheModule) Dependencies() []string

Dependencies returns the names of modules this module depends on. The cache module has no dependencies and can be started independently.

func (*CacheModule) Flush

func (m *CacheModule) Flush(ctx context.Context) error

Flush removes all items from the cache. This operation is irreversible and should be used with caution. Useful for cache invalidation or testing scenarios.

Example:

err := cache.Flush(ctx)
if err != nil {
    // handle flush error
}

func (*CacheModule) Get

func (m *CacheModule) Get(ctx context.Context, key string) (interface{}, bool)

Get retrieves a cached item by key. Returns the cached value and a boolean indicating whether the key was found. If the key doesn't exist or has expired, returns (nil, false).

Example:

value, found := cache.Get(ctx, "user:123")
if found {
    user := value.(UserData)
    // process user data
}

func (*CacheModule) GetMulti

func (m *CacheModule) GetMulti(ctx context.Context, keys []string) (map[string]interface{}, error)

GetMulti retrieves multiple items from the cache in a single operation. Returns a map of key-value pairs for found items and an error if the operation fails. Missing keys are simply not included in the result map.

Example:

keys := []string{"user:123", "user:456", "user:789"}
results, err := cache.GetMulti(ctx, keys)
if err != nil {
    // handle error
}
for key, value := range results {
    // process found values
}

func (*CacheModule) Init

func (m *CacheModule) Init(app modular.Application) error

Init initializes the cache module with the application context. This method is called after all modules have been registered and their configurations loaded. It sets up the cache engine based on the configuration.

The initialization process:

  1. Retrieves the module's configuration
  2. Sets up logging
  3. Initializes the appropriate cache engine (memory or Redis)
  4. Logs the initialization status

Supported cache engines:

  • "memory": In-memory cache with LRU eviction
  • "redis": Redis-based distributed cache
  • fallback: defaults to memory cache for unknown engines

func (*CacheModule) Name

func (m *CacheModule) Name() string

Name returns the unique identifier for this module. This name is used for service registration, dependency resolution, and configuration section identification.

func (*CacheModule) ProvidesServices

func (m *CacheModule) ProvidesServices() []modular.ServiceProvider

ProvidesServices declares services provided by this module. The cache module provides a cache service that can be injected into other modules.

Provided services:

  • "cache.provider": The main cache service interface

func (*CacheModule) RegisterConfig

func (m *CacheModule) RegisterConfig(app modular.Application) error

RegisterConfig registers the module's configuration structure. This method is called during application initialization to register the default configuration values for the cache module.

Default configuration:

  • Engine: "memory"
  • DefaultTTL: 300 seconds (5 minutes)
  • CleanupInterval: 60 seconds (1 minute)
  • MaxItems: 10000
  • Redis settings: empty/default values

func (*CacheModule) RequiresServices

func (m *CacheModule) RequiresServices() []modular.ServiceDependency

RequiresServices declares services required by this module. The cache module operates independently and requires no external services.

func (*CacheModule) Set

func (m *CacheModule) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error

Set stores an item in the cache with an optional TTL. If ttl is 0, uses the default TTL from configuration. The value can be any serializable type.

Example:

// Use default TTL
err := cache.Set(ctx, "user:123", userData, 0)

// Use custom TTL
err := cache.Set(ctx, "session:abc", sessionData, time.Hour)

func (*CacheModule) SetMulti

func (m *CacheModule) SetMulti(ctx context.Context, items map[string]interface{}, ttl time.Duration) error

SetMulti stores multiple items in the cache in a single operation. All items will use the same TTL. If ttl is 0, uses the default TTL from configuration. This is more efficient than multiple Set calls for batch operations.

Example:

items := map[string]interface{}{
    "user:123": userData1,
    "user:456": userData2,
    "session:abc": sessionData,
}
err := cache.SetMulti(ctx, items, time.Minute*30)

func (*CacheModule) Start

func (m *CacheModule) Start(ctx context.Context) error

Start performs startup logic for the module. This method establishes connections to the cache backend and prepares the cache for operations. It's called after all modules have been initialized.

For memory cache: No external connections are needed For Redis cache: Establishes connection pool to the Redis server

func (*CacheModule) Stop

func (m *CacheModule) Stop(ctx context.Context) error

Stop performs shutdown logic for the module. This method gracefully closes all connections and cleans up resources. It's called during application shutdown to ensure proper cleanup.

The shutdown process:

  1. Logs the shutdown initiation
  2. Closes cache engine connections
  3. Cleans up any background processes

type MemoryCache

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

MemoryCache implements CacheEngine using in-memory storage

func NewMemoryCache

func NewMemoryCache(config *CacheConfig) *MemoryCache

NewMemoryCache creates a new memory cache engine

func (*MemoryCache) Close

func (c *MemoryCache) Close(_ context.Context) error

Close stops the memory cache cleanup routine

func (*MemoryCache) Connect

func (c *MemoryCache) Connect(ctx context.Context) error

Connect initializes the memory cache

func (*MemoryCache) Delete

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

Delete removes an item from the cache

func (*MemoryCache) DeleteMulti

func (c *MemoryCache) DeleteMulti(ctx context.Context, keys []string) error

DeleteMulti removes multiple items from the cache

func (*MemoryCache) Flush

func (c *MemoryCache) Flush(_ context.Context) error

Flush removes all items from the cache

func (*MemoryCache) Get

func (c *MemoryCache) Get(_ context.Context, key string) (interface{}, bool)

Get retrieves an item from the cache

func (*MemoryCache) GetMulti

func (c *MemoryCache) GetMulti(ctx context.Context, keys []string) (map[string]interface{}, error)

GetMulti retrieves multiple items from the cache

func (*MemoryCache) Set

func (c *MemoryCache) Set(_ context.Context, key string, value interface{}, ttl time.Duration) error

Set stores an item in the cache

func (*MemoryCache) SetMulti

func (c *MemoryCache) SetMulti(ctx context.Context, items map[string]interface{}, ttl time.Duration) error

SetMulti stores multiple items in the cache

type RedisCache

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

RedisCache implements CacheEngine using Redis

func NewRedisCache

func NewRedisCache(config *CacheConfig) *RedisCache

NewRedisCache creates a new Redis cache engine

func (*RedisCache) Close

func (c *RedisCache) Close(ctx context.Context) error

Close closes the connection to Redis

func (*RedisCache) Connect

func (c *RedisCache) Connect(ctx context.Context) error

Connect establishes connection to Redis

func (*RedisCache) Delete

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

Delete removes an item from the Redis cache

func (*RedisCache) DeleteMulti

func (c *RedisCache) DeleteMulti(ctx context.Context, keys []string) error

DeleteMulti removes multiple items from the Redis cache

func (*RedisCache) Flush

func (c *RedisCache) Flush(ctx context.Context) error

Flush removes all items from the Redis cache

func (*RedisCache) Get

func (c *RedisCache) Get(ctx context.Context, key string) (interface{}, bool)

Get retrieves an item from the Redis cache

func (*RedisCache) GetMulti

func (c *RedisCache) GetMulti(ctx context.Context, keys []string) (map[string]interface{}, error)

GetMulti retrieves multiple items from the Redis cache

func (*RedisCache) Set

func (c *RedisCache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error

Set stores an item in the Redis cache with a TTL

func (*RedisCache) SetMulti

func (c *RedisCache) SetMulti(ctx context.Context, items map[string]interface{}, ttl time.Duration) error

SetMulti stores multiple items in the Redis cache with a TTL

Jump to

Keyboard shortcuts

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