Backends & engines

The two cache backends (memory + RESP) and the five Redis-compatible engines they cover — Redis, Valkey, KeyDB, Dragonfly, Upstash — plus every env var and connection preset.

The backend is chosen by environment variable (or app.config.ts) and is shared by both caching modes. Everything else — wrap, the cache: query field, tag invalidation — is backend-agnostic.

Selecting a backend

CACHE_BACKEND       memory | redis            (default: memory)
CACHE_REDIS_URL     redis/rediss/REST url     (falls back to REDIS_URL)
CACHE_REDIS_DRIVER  resp | http               (default: resp; http = Upstash REST, for edge)
CACHE_REDIS_TOKEN   http-driver auth          (falls back to UPSTASH_REDIS_REST_TOKEN)
CACHE_KEY_PREFIX    namespace                 (redis backend only; default: voltro:cache)
CACHE_MAX_ENTRIES   memory capacity cap       (optional, memory backend only)

CACHE_KEY_PREFIX is consumed only by the redis backend (it namespaces the RESP keyspace). Under CACHE_BACKEND=memory it parses but is a no-op — the memory map isn't a shared keyspace, so there is nothing to prefix.

// app.config.ts — env always wins over this
export default { type: 'api', name: 'myApi', store: 'postgres', cache: 'redis' }

The resolved backend is printed at boot — voltro logs --tail 50 | grep 'cache backend':

[voltro:dev] cache backend resolved: redis (driver resp)

memory (default)

In-process Map with lazy expiry. Single instance only — entries live in the process, invisible to other nodes. Zero-config, perfect for dev and single-pod deployments. CACHE_MAX_ENTRIES bounds it for write-heavy workloads. Resets on restart.

Capacity eviction is oldest-by-insertion (FIFO) — reads do not bump recency, so under cap pressure a hot frequently-read entry is dropped before a cold never-read one. This is NOT LRU: it's a deliberate simplicity trade for a single-process dev/cache tier (the cross-instance tier is redis). Size the cap above your hot-set, or rely on per-entry TTL, if that matters.

Use it when: you're in dev, or you run exactly one app instance and don't need the cache to survive a restart.

redis — one backend, five engines

The redis backend speaks the RESP wire protocol, so a single implementation drives five engines with no per-engine code:

Engine Driver Notes
Redis resp (TCP) The reference implementation.
Valkey resp (TCP) The Linux-Foundation fork of Redis — drop-in.
KeyDB resp (TCP) Multithreaded Redis fork — drop-in.
Dragonfly resp (TCP) Modern high-throughput drop-in.
Upstash resp (TCP) or http (REST) Use http for serverless/edge runtimes where a persistent TCP socket isn't viable.

Connection presets:

# Redis / Valkey / KeyDB / Dragonfly (all identical — just point at the server)
CACHE_BACKEND=redis CACHE_REDIS_URL=redis://localhost:6379

# TLS
CACHE_REDIS_URL=rediss://default:password@host:6379

# Upstash over the edge HTTP/REST API
CACHE_BACKEND=redis \
CACHE_REDIS_DRIVER=http \
CACHE_REDIS_URL=https://your-db.upstash.io \
CACHE_REDIS_TOKEN=********

Use it when: you run more than one app instance (k8s replicas, multi-PM2, ECS tasks) and need a shared cache, or you want the cache to survive restarts.

Storage layout

Under CACHE_KEY_PREFIX (default voltro:cache):

  • value keys → voltro:cache:v:<key> (TTL via PSETEX; JSON-encoded)
  • tag sets → voltro:cache:t:<tag> (a Redis SET of the raw keys carrying that tag)

clear() is scoped to the prefix via SCAN — it never runs FLUSHDB, so a Redis shared with other framework state (e.g. the read-replica RYW store) is safe.

Reference semantics differ by backend. The memory backend returns the same object reference you stored (fast, but mutating a cached object mutates the cache — don't). The Redis backends JSON round-trip, so they always return a fresh copy. Treat cached values as immutable on both.

Choosing

  • One instance / devmemory. Nothing to run.
  • Multiple instances, or restart-survivalredis (pick any of the five engines by URL).
  • Serverless / edge (no persistent TCP) → redis with CACHE_REDIS_DRIVER=http (Upstash).

To provision the infra (a Redis container in compose / a Deployment in helm) plus the env, see Enabling Redisvoltro add redis wires it all.