How to Configure Redis Cache Backend in Gorig Using the Cache Module

To configure Redis in Gorig, set the redis.addr, redis.password, and redis.db configuration keys, then instantiate a cache using cache.New[T](cache.Redis) which returns a generic RedisCache[T] wrapper backed by go-redis/v8.

Gorig provides a generic, multi-backend cache subsystem that supports in-memory, file-based, SQLite, and Redis storage. When you configure Redis cache backend in Gorig using the cache module, the framework creates a singleton redis.Client from the go-redis/v8 library and wraps it in a type-safe RedisCache[T] that implements the Cache[T] interface.

Configuration Prerequisites

Gorig reads Redis connection parameters via the configuration helper in utils/cofigure/cfg.go. The factory function initRedisCache in cache/cache.redis.go expects three specific keys to build the redis.Options struct.

Required Configuration Keys

Config Key Purpose Default Behavior
redis.addr Redis server address in host:port format If empty or omitted, Redis is disabled and initialization is skipped
redis.password Password for Redis AUTH command Empty string (no authentication)
redis.db Logical database number as a string "0" (converted to integer 0)

Setting Configuration Values

You can provide these values via environment variables or JSON/YAML configuration files. The configure package automatically maps environment variables to these keys.

Using environment variables:

export REDIS_ADDR="127.0.0.1:6379"
export REDIS_PASSWORD="mySecret"
export REDIS_DB="0"

Using a YAML configuration file:

redis:
  addr: "127.0.0.1:6379"
  password: "mySecret"
  db: "0"

Instantiating the Redis Cache

Once the configuration is loaded—typically during bootstrap/startup.go execution—you create a Redis-backed cache through the public API in cache/cache.go.

The Factory Function

The generic constructor cache.New[T any] selects the backend based on the cache.Type argument. Passing cache.Redis triggers the Redis initialization path:

import "github.com/jom-io/gorig/cache"

// Create a Redis-backed cache for a custom type
userCache := cache.New[UserProfile](cache.Redis)

Internally, this calls GetRedisInstance[T] (defined in cache/cache.redis.go), which lazily builds the *redis.Client if it does not already exist and wraps it in a *RedisCache[T].

The RedisCache Wrapper

RedisCache[T] implements the Cache[T] interface, providing methods like Set, Get, LPop, and RPush. All values are automatically serialized to JSON before storage and deserialized on retrieval, allowing you to cache any struct that can be marshaled.

Architecture and Implementation Details

Understanding the internal flow helps debug connection issues and optimize usage.

Lazy Singleton Initialization

  1. First Call: When cache.New[T](cache.Redis) is invoked, GetRedisInstance checks the package-level redisInstance variable.
  2. Client Construction: If nil, it calls initRedisCache, which reads the three redis.* config keys via configure.GetString and creates a redis.Client.
  3. Reuse: The client is stored in redisInstance for the process lifetime, ensuring all cache instances share a single connection pool.
  4. Disabled State: If redis.addr is empty, the initialization skips client creation, allowing the application to start without Redis.

Serialization Strategy

All operations in RedisCache[T] (see Set, Get, RPush in cache/cache.redis.go) use JSON marshaling. This design choice enables the message broker in mid/messagex/broker.simple.go to store complex message structs in Redis lists transparently.

Practical Code Examples

Basic Redis Setup with Environment Variables

Ensure your application loads configuration before creating caches:

package main

import (
    "fmt"
    "time"
    "github.com/jom-io/gorig/cache"
)

type Product struct {
    SKU  string  `json:"sku"`
    Price float64 `json:"price"`
}

func main() {
    // Configuration is loaded automatically from env vars or config files
    
    // Create a Redis cache for Product structs
    productCache := cache.New[Product](cache.Redis)
    
    // Store with 5-minute TTL
    productCache.Set("sku:123", Product{SKU: "123", Price: 99.99}, 5*time.Minute)
    
    // Retrieve
    prod, err := productCache.Get("sku:123")
    if err != nil {
        fmt.Println("Cache miss or error:", err)
        return
    }
    fmt.Printf("Cached product: %+v\n", prod)
}

Multi-Level Caching Strategy

Combine in-memory speed with Redis durability using cache.Tool:

func NewProductCacheTool() *cache.Tool[Product] {
    // L1: Fast in-memory cache with 1-minute TTL
    memCache := cache.New[Product](cache.Memory, time.Minute)
    
    // L2: Shared Redis cache
    redisCache := cache.New[Product](cache.Redis)
    
    // Loader fetches from database on cache miss
    loader := func(key string) (Product, error) {
        // Database fetch logic here
        return Product{SKU: key, Price: 0.0}, nil
    }
    
    return cache.NewCacheTool[Product](
        context.Background(),
        []cache.Cache[Product]{memCache, redisCache},
        loader,
    )
}

Using Redis as a Message Queue

The messagex package uses the Redis cache internally for distributed messaging:

import "github.com/jom-io/gorig/mid/messagex"

// Creates a broker that uses Redis lists (RPush/BRPop) for queuing
broker := messagex.NewSimpleByType(messagex.Redis)

This leverages RedisCache[Message] to serialize messages as JSON before pushing them to Redis queues.

Summary

  • Set redis.addr to enable Redis; if omitted, the cache module skips Redis initialization entirely.
  • Use cache.New[T](cache.Redis) to obtain a type-safe RedisCache[T] instance that implements the generic Cache[T] interface.
  • Configuration is read from environment variables or config files via utils/cofigure/cfg.go, supporting redis.addr, redis.password, and redis.db.
  • JSON serialization is handled automatically by the wrapper, allowing complex Go structs to be stored directly.
  • Singleton pattern ensures efficient connection reuse through GetRedisInstance in cache/cache.redis.go.
  • Integration with higher-level components like the message broker in mid/messagex/broker.simple.go works transparently.

Frequently Asked Questions

What happens if redis.addr is not configured?

If redis.addr is empty or undefined, initRedisCache in cache/cache.redis.go skips client creation entirely. The application starts successfully but Redis caching is unavailable; components attempting to use it may encounter errors or fall back to alternative cache types depending on implementation.

How does Gorig handle Redis authentication?

The redis.password config key is passed directly to the redis.Options struct when constructing the client in cache/cache.redis.go. If left empty, the client connects without authentication. For production environments, set REDIS_PASSWORD or the equivalent config file key.

Can I use multiple Redis databases in the same application?

The current implementation in cache/cache.redis.go maintains a single global redisInstance configured with one redis.db value. To use multiple databases simultaneously, you would need to instantiate separate redis.Client instances manually, as the singleton pattern assumes a single shared connection pool.

Does RedisCache support complex Go types?

Yes. RedisCache[T] uses JSON marshaling for all values via the standard encoding/json package. As long as your type T is JSON-serializable (structs with exported fields, basic types, slices, maps), it can be cached. The wrapper handles serialization in methods like Set and deserialization in Get automatically.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →