Skip to main content

Redis & Caching

Redis serves as the ephemeral data layer: query caching, rate limiting, session preferences, and async event delivery via Streams. All data in Redis is expendable — losing it causes cache misses, not data loss.

Engine: Redis 7
Port: 6380 (offset from FalkorDB on 6379)

Not FalkorDB

Redis (port 6380) is the application cache. FalkorDB (port 6379) is the graph database. They are separate Redis instances. Never confuse them.

Cache Key Format

All cache keys follow the pattern: gl:<resource>:<id>:<params>

Content Query Cache

Key PatternTTLDescription
gl:passage:<passageId>[:<suffix>]3600s (1h)Single passage; suffix = witnesses, words, xrefs, commentary
gl:chapter:<bookId>.<chapter>:<translation>3600s (1h)Full chapter
gl:topic:<topicId>3600s (1h)Topic with passage list
gl:lexicon:<wordId>86400s (24h)Lexicon entry (rarely changes)

Immutable scripture content is cached for 1 hour. Lexicon entries, which change even less frequently, are cached for 24 hours.

Entitlement Cache

Key PatternTTLDescription
gl:entitlements:<planId>:<feature>60sPlan feature access ("1" or "0")

Short TTL ensures plan changes propagate within a minute.

AI Response Cache

Key PatternTTLDescription
gl:ai:cache:<promptHash>3600s (1h)Cached LLM response (semantic caching)

Avoids redundant LLM API calls for similar prompts. The prompt hash covers the system prompt, user messages, and context.

Session Preferences

Key PatternTTLDescription
gl:session:<userId>:prefs604800s (7d)User display preferences (JSON)

Clerk manages primary sessions; Redis stores device-specific preferences (font size, reading mode, etc.).

Rate Limiting

Rate limit state is stored in Redis using a token bucket algorithm:

gl:ratelimit:<userId>:<endpoint>:<window_start> → INT (request count)

Rate Limit Tiers

EndpointFreePaid (10×)AI
Search20/min200/min
Passages60/min600/min
Lexicon40/min400/min
AI requests5/hour50/hour200/hour

The gateway checks rate limits before proxying. Limits are per-user when authenticated, per-IP for unauthenticated requests.

Redis Streams (Event Queue)

Redis Streams provide at-least-once delivery for async events between services. Each stream has one or more consumer groups.

Streams

StreamPublishersConsumersPurpose
gl:events:notificationsAny serviceNotificationsPush/email dispatch
gl:events:usersAuth serviceMultipleUser lifecycle (created, updated)
gl:events:ingestIngest pipelineContentIndex refresh triggers

Consumer Group Pattern

XREADGROUP GROUP notifications-workers consumer-1
STREAMS gl:events:notifications >
COUNT 10
BLOCK 5000

Each notification worker reads up to 10 messages, processes them (one goroutine per message), and ACKs on success. Failed messages are retried via the pending entries list (PEL).

Distributed Locks

The ingest pipeline uses Redis locks to prevent concurrent writes:

gl:lock:ingest:<schemaName> → STRING (120s TTL)

Locks auto-expire after 120 seconds to prevent deadlocks if the ingest process crashes.

Hosting Progression

PhaseProviderInstance
Phase 1DockerSingle instance
Phase 2ElastiCachecache.r6g.large
Phase 3ElastiCache Cluster ModeMulti-node