Skip to content
Warlock.js v4

Choosing a Driver

@warlock.js/cache ships eight drivers. They all speak the same API — you can swap one for another without touching call sites — but they trade off durability, scale, and capabilities. This page helps you pick.

Your situationDriver
Local dev, single processmemory
Session-style data that should stay alive while usedmemoryExtended
Bounded memory footprint (cap the entry count)lru
Small persistent cache, no server (CLI, build cache)file
Production, multiple instances, shared cache + locksredis
You already run Postgres, or need vector similaritypg
Disable caching without changing codenull
Asserting cache behavior in testsmock
  1. Running more than one instance? You need a shared store — go redis (or pg if Postgres is already in your stack). In-memory drivers are per-process, so each instance would hold its own copy and invalidation wouldn’t propagate.
  2. Need vector similarity in production? pg with the vector config block — real pgvector index, same cache.similar() code.
  3. Single process, just want speed? memory. Add maxSize to cap it, or reach for lru when you want a hard entry ceiling with eviction.
  4. Need it to survive a restart but don’t want a server? file.
DriverSurvives restartMulti-instanceNative locksNative listsSimilarityEviction
memoryemulatedread-modify-writebrute forcemaxSize (LRU)
memoryExtendedemulatedread-modify-writebrute forcemaxSize (LRU)
lruemulatedread-modify-writebrute forcecapacity (LRU)
file⚠️ same hostemulatedread-modify-writeTTL only
redisnative NX/XXread-modify-write¹(backlog)TTL / Redis policy
pgemulatedread-modify-write✅ pgvectorTTL only
nulln/an/an/an/areturns []n/a

¹ Lists are stored as a single JSON entry and mutated read-modify-write on every driver today. Native Redis list commands (LPUSH / LRANGE / LTRIM) are planned for v2.1.

:::note update() / merge() support The file driver throws CacheUnsupportedError for update() and merge() (no atomic read-modify-write primitive on disk yet). All other drivers support them. See Update & Merge. :::

  • memory — an in-process Map. Fastest option, zero setup, gone on restart. The default for dev and single-instance apps. Optional maxSize evicts the least-recently-used entry past the limit.
  • memoryExtendedmemory with sliding expiration: every read refreshes the entry’s TTL. Use it for data that should stay cached as long as it’s actively used (session-ish reads) and expire only after a quiet period.
  • lru — a hard capacity ceiling (default 1000) with least-recently-used eviction. Reach for it when bounding memory matters more than keeping every entry.
  • file — JSON-on-disk. Survives restarts without running a server, but it’s single-host and doesn’t support update()/merge(). Good for CLIs and build caches.
  • redis — the production workhorse. Shared across instances, native SET NX/XX for real distributed locks, native list commands for O(1) list ops. Similarity isn’t wired yet.
  • pg — Postgres-backed and durable. The only driver with a real similarity index (pgvector, HNSW or IVFFlat). Bring your own pg.Pool/Client; the driver doesn’t own the connection lifecycle.
  • null — a black hole. Every write is a no-op, every read a miss. Swap it in to disable caching globally without touching call sites.
  • mock — an in-memory store plus a callLog of every operation, for behavioral assertions in tests. See Testing.

You’re not locked into one. Register several and route per call with the driver option — keep your default hot path in memory while sending durable or audit writes to redis:

await cache.set("user.1", user, "1h"); // default driver
await cache.set("audit.event", event, { driver: "redis", ttl: "30d" });

See Configuration for registering drivers and Set Options for per-call overrides.