Skip to main content

Redis Caching System

Overview

CONA uses Redis as an in-memory caching layer to significantly improve application performance by reducing database query latency. Redis stores frequently accessed data in memory, providing sub-millisecond access times for cached data.

Why Redis?

Performance Benefits

  • Query Latency Reduction: Reduces database query time from ~100ms to ~5ms
  • Workflow Throughput: Improves Temporal workflow performance by reducing I/O bottlenecks
  • User Experience: Faster page loads and real-time data updates
  • Cost Efficiency: Reduces database load, allowing for smaller database instances

Use Cases in CONA

  1. Chart of Accounts Caching: Frequently accessed account data and lists
  2. Organization Settings: User preferences and configuration data
  3. Posting Matrix Rules: Complex accounting rules that are expensive to compute
  4. Integration Data: Cached results from external API calls
  5. Session Data: User authentication and session information

Architecture

This documentation provides comprehensive coverage of Redis usage in CONA, including:
  1. Why Redis is used - Performance benefits and use cases
  2. Architecture overview - How Redis fits into the system
  3. Configuration details - Production and development setup
  4. Implementation patterns - Code examples and best practices
  5. Cache version management - Automatic stale cache detection and invalidation
  6. Monitoring integration - How to connect with Redis Insights
  7. Troubleshooting guide - Common issues and solutions
  8. Security considerations - Access control and data protection
  9. Cost optimization - Current costs and optimization strategies

Redis Configuration

Production Environment

Provider: Redis Cloud Configuration:
  • Database: 13661105
  • Subscription: 2958056
  • Region: Frankfurt (fra)
  • Memory: 250MB (Dataset: 125MB, Total: 250MB)
  • High Availability: Single zone
  • Connections: 30 concurrent connections
  • Persistence: RDB snapshots every 6 hours
  • Network Cap: 100GB/month
Connection Details:
Connection string - rediss://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT} GUI to connect - Redis Insight

Development Environment

Local Redis Instance: Local instances is running in docker container. To spin up the instances run pnpm run redis:start:dev

Environment Variables

Required Variables:
  • REDIS_HOST - Redis server hostname
  • REDIS_PORT - Redis server port
  • REDIS_PASSWORD - Redis authentication password
  • REDIS_TLS - Set to "true" for TLS connections (production)
  • REDIS_ENV - Environment identifier for cache key prefixing (defaults to no prefix in production)
Optional Variables:
  • REDIS_ENABLED - Set to "false" to disable Redis (all cache operations will be no-ops)
  • REDIS_CACHE_VERSION - Manual cache version override (highest priority)
    • If not set, automatically uses VERCEL_GIT_COMMIT_SHA (first 8 chars) on Vercel
    • Falls back to "1" if neither is available
    • Used to detect stale cache after Redis restarts

Cache Version Management

CONA implements automatic cache version management to prevent serving stale data after Redis restarts or deployments.

How It Works

  1. On Redis Startup: The system checks the current cache version stored in Redis
  2. Version Comparison: Compares stored version with expected version from environment
  3. Automatic Flush: If versions don’t match, the entire cache is flushed to prevent stale data
  4. Version Update: The new version is stored in Redis for future checks

Version Sources (Priority Order)

  1. Manual Override (REDIS_CACHE_VERSION): Highest priority, set explicitly
  2. Vercel Git Commit (VERCEL_GIT_COMMIT_SHA): Automatically available on Vercel deployments
  3. Default Fallback ("1"): Used when no other version is available

Configuration Examples

Production (Automatic):
Manual Version Control:
Development:

When Cache Gets Flushed

  • Deployment: New VERCEL_GIT_COMMIT_SHA triggers automatic flush
  • Manual Version Change: Updating REDIS_CACHE_VERSION flushes cache
  • First Run: Initial Redis connection sets version (no flush)
  • Version Mismatch: Any mismatch between stored and expected version
Every flush emits a redis.cache_flush event to Axiom at info level with the old version, new version, and the number of keys wiped (keyCount from a DBSIZE call immediately before FLUSHDB). Use the cache flush history Axiom query to audit when flushes occur in production.

Implementation

Import Redis Functions

Redis Client Initialization

Redis is automatically initialized on application startup via the startRedis() function. This function:
  • Connection Management: Handles Redis connection with automatic retry logic
  • Error Handling: Gracefully handles connection failures and returns status
  • Version Checking: Automatically checks and updates cache version on connection
  • Reconnection: Implements exponential backoff retry strategy (4 attempts max)
  • Return Value: Returns a RedisStartResult object with connection status
Connection Retry Strategy:
  • Initial delay: 4 seconds
  • Max attempts: 4
  • Backoff: Geometric progression (4s, 8s, 16s, 32s)
  • Total timeout: ~65 seconds (sum of all delays + 5s buffer)

Core Cache Functions

All cache functions include:
  • Graceful Degradation: Return null or no-op if Redis is unavailable
  • Environment Prefixing: Automatically prefix keys with REDIS_ENV in non-production
  • Error Handling: Log warnings but don’t throw errors (allows app to continue)
Environment Key Prefixing: Keys are automatically prefixed with REDIS_ENV in non-production environments:

Cache Key Conventions

Key Structure: {domain}:{type}:{organizationId}:{identifier}

Bulk Cache Invalidation Functions

CONA provides specialized functions for invalidating related caches:

Usage Patterns

1. Cache-Aside Pattern

2. Write-Through Pattern

3. Cache Invalidation Strategies

Monitoring & Debugging

Redis Insights Integration

Production Redis Cloud Dashboard: https://cloud.redis.io/#/databases/13661105/subscription/2958056/view-bdb/configuration Key Metrics to Monitor:
  • Memory Usage: Track current / 250MB — check Redis Cloud dashboard
  • Hit Rate: Target >90% cache hit rate
  • Connection Count: Monitor concurrent connections
  • Key Count: Track number of cached keys
  • Operations per Second: Monitor Redis performance
  • Network Usage: Monitor against the 100 GB/month cap — use the redis.usage_rollup Axiom queries above for per-service and per-key-family breakdowns
  • Cache Version: Check cache:version key to see current version
  • Cache Flushes: Query redis.cache_flush events in Axiom to track when and how many keys are wiped on each deploy

Redis CLI Access

Application Logging

The Redis implementation includes comprehensive logging:
  • Connection Events: Logs when Redis connects, reconnects, or fails (redis.connection → Axiom)
  • Version Checks: Logs cache version initialization and mismatches
  • Cache Flushes: Emits a redis.cache_flush event to Axiom with keyCount, old/new version
  • Aggregate Usage: Emits redis.usage_rollup events periodically (see Axiom Observability section)
  • Error Handling: Logs warnings when cache operations fail (allows graceful degradation)
Check application logs for messages like:
  • "Redis connection established successfully"
  • "Cache version initialized"
  • "Cache version mismatch - flushing cache"
  • "Cache flushed due to version mismatch"
In Axiom, prefer querying the structured events (redis.usage_rollup, redis.cache_flush) over free-text log lines — they are always present regardless of log level filtering.

Axiom Observability

Every Redis operation emits structured telemetry to Axiom. This data is available now in the live datasets and can be queried immediately to investigate performance, cache efficiency, and network usage — without any code changes. Four event types are written under the fields.event key: redis.call events are emitted at debug level. In long-running Temporal workers, the pino logger typically runs at info, so per-call debug events are dropped there. redis.usage_rollup fills that gap — it aggregates the same data and emits at info so it always reaches Axiom.

Setup

Call configureRedisObservability() once at process startup, after startRedis(), to wire the correct service identity and logger into all subsequent events. Without this call the events fall back to consoleLogger and never reach Axiom.
Options: The state is stored on globalThis via Symbol.for("cona.redis.observability") so it is shared across all Next.js bundles in the same Node process — the instrumentation file and route handlers always see the same configuration. Shutting down (Temporal workers only): Call flushRedisRollup() before the final pino drain so the last partial window is shipped to Axiom before process.exit() kills the transport:

redis.call Event Schema

Emitted by observeRedisCall() which wraps every Redis command. Each field maps directly to an Axiom column under fields.*. Key prefix extraction strips everything after the first colon so dynamic segments (org IDs, user tokens, etc.) are never exposed in logs:

redis.connection Event Schema

Emitted by attachRedisConnectionLifecycleListeners() on every ioredis lifecycle transition.

redis.usage_rollup Event Schema

Emitted at info level by the periodic rollup timer and by flushRedisRollup() at shutdown. Each event covers one (service, caller, operation, keyPrefix) bucket for a single time window.

redis.cache_flush Event Schema

Emitted at info level by checkCacheVersion() whenever a deploy-time FLUSHDB is triggered. Always captured in both webapp and workers.

Privacy Guarantees

The observability layer is designed so that raw Redis keys, values, tokens, session IDs, and any PII are never written to Axiom. Only the following are logged:
  • Byte countsrequestBytes, responseBytes, totalBytes
  • Normalised key prefix — the static first segment of the key, not the full key
  • Error messages — stripped of key names and raw values

Example Axiom Queries

Use the cona_webapp dataset for webapp events. For Temporal worker events use cona_temporal-workers (note: redis.call debug events are dropped at the worker’s pino level — use redis.usage_rollup events from that dataset instead). Cache hit rate over time
Slowest Redis operations (p95 duration)
Network bytes sent to Redis per key family
Recent connection lifecycle events (detect reconnect storms)
All Redis errors in the last hour
Volume of SET operations by TTL bucket
Aggregate network usage per service (rollup — works for both webapp and workers)
Top bandwidth consumers by key family (rollup)
Rollup over the Temporal workers dataset
Cache flush history (when and how many keys were wiped)

Best Practices

1. TTL (Time To Live) Strategy

2. Cache Key Design

  • Hierarchical: Use colons to separate levels (domain:type:org:id)
  • Consistent: Follow the same pattern across all cache keys
  • Descriptive: Make keys self-documenting
  • Environment-aware: Automatically prefixed with REDIS_ENV in non-production

3. Error Handling

All cache functions are designed to fail gracefully:

4. Memory Management

  • Set appropriate TTLs to prevent memory bloat
  • Use bulk invalidation functions for related caches
  • Monitor memory usage regularly in Redis Insights
  • Cache version management automatically prevents stale data accumulation

5. Cache Version Management

  • Automatic on Vercel: No configuration needed - uses git commit SHA
  • Manual for migrations: Set REDIS_CACHE_VERSION when deploying schema changes
  • Development: Uses default version or set manually for testing
  • Monitor logs: Watch for version mismatch warnings

Troubleshooting

Common Issues

  1. Connection Timeouts
    • Check Redis Cloud status
    • Verify network connectivity
    • Check connection pool settings
    • Review retry logs (max 4 attempts with exponential backoff)
  2. Memory Issues
    • Monitor memory usage in Redis Insights
    • Check for memory leaks in key patterns
    • Adjust TTL values
    • Use bulk invalidation functions
  3. Performance Issues
    • Check cache hit rates
    • Monitor Redis CPU usage
    • Verify key patterns are efficient
    • Check for version mismatch flushes (may cause temporary performance hit)
  4. Data Inconsistency
    • Ensure proper cache invalidation
    • Check for race conditions
    • Verify write-through patterns
    • Review cache version logs for unexpected flushes
  5. Stale Cache After Restart
    • Cache version management should automatically handle this
    • Check logs for version mismatch warnings
    • Verify REDIS_CACHE_VERSION or VERCEL_GIT_COMMIT_SHA is set correctly
    • Manually flush if needed: redis-cli FLUSHDB

Debug Commands

Security

Access Control

  • Password Protection: All Redis instances use strong passwords
  • TLS Encryption: Production Redis uses TLS for secure connections
  • Network Isolation: Redis Cloud provides network-level security
  • Environment Separation: Development and production use separate instances
  • Key Prefixing: Non-production environments use REDIS_ENV prefix to prevent cross-contamination

Data Protection

  • No Sensitive Data: Never cache passwords, tokens, or PII
  • TTL Enforcement: All cached data has appropriate expiration
  • Encryption: Sensitive cached data should be encrypted before storage
  • Version Management: Automatic cache flushing prevents stale sensitive data

Cost Optimization

Current Costs

  • Redis Cloud: ~$10/month for 250MB memory (estimated based on usage)
  • Performance Gain: Reduces database load, allowing smaller DB instances
  • ROI: Significant performance improvement for minimal cost

Optimization Strategies

  1. Right-size Memory: Monitor usage and adjust memory allocation
  2. Efficient TTLs: Set appropriate expiration times
  3. Key Compression: Use shorter, efficient key names
  4. Pattern Cleanup: Regular cleanup of unused keys
  5. Version Management: Automatic flushing prevents accumulation of stale data

Future Enhancements

Planned Features

  1. Cache Warming: Pre-populate cache with frequently accessed data
  2. Distributed Caching: Support for multiple Redis instances
  3. Cache Analytics: Detailed hit/miss ratio tracking
  4. Automatic Scaling: Dynamic memory allocation based on usage
  5. Cache Versioning: Enhanced version management with metadata tracking

Integrations

  1. Temporal Workflows: Cache workflow state and results
  2. API Rate Limiting: Use Redis for rate limiting external APIs
  3. Session Management: Store user sessions in Redis
  4. Real-time Features: Use Redis for pub/sub messaging