LazyLayersv0.5.3
Setups

Serverful

Traditional servers, Docker, and Kubernetes where the process persists long enough for L1 to warm across requests.

A serverful platform gives you a long-running process. It handles many requests, its heap survives between them, and an LRU cache inside it warms as traffic arrives. This is where both L1 and L2 earn their keep.

L1 answers "how fast is a repeat read on this server". L2 answers "what does the fleet agree on, and what survives a deploy". Neither answer degrades the other.

The two shapes

Serverful setups come in two shapes, and the right one depends on how many processes read the same data.

When to use which

ScenarioSetupWhy
One process serves all trafficSingle processNo peers to share with, no bus to operate
Behind a load balancer with 2+ replicasMultiple instancesL2 absorbs misses across servers, the bus keeps L1s in sync
Under Node.js clusteringMultiple instancesEvery worker is a separate process with its own L1 and its own identity
Docker Compose with service replicasMultiple instancesEach container is a separate cache that must agree with the others
Kubernetes podsMultiple instancesPod names make stable, unique sources

The progression

Start with single process

One file, one cache, L1 only. You get in-flight dedupe, a warm LRU, and fail-open behaviour for free. Nothing to operate.

If your traffic is served by one process and a cold start is acceptable, this is enough.

Graduate to multiple instances when you need to

Move on when any of these is true:

  • You run more than one process against the same data
  • Your L1 TTL has to be shorter than your traffic can afford
  • Cold starts are visible to users
  • One key is expensive enough that duplicating the work across processes matters

That takes two files: src/cache/redis.js for L2 and src/cache/bus.js for the event bus. The single-process cache file gets rewired to use them.

Apply the production checklist when you ship

Instance sizing, Node.js clustering, graceful shutdown, compression, and the transport tabs in full. This is the hardening pass before real traffic.

What you keep across both shapes

  • In-flight dedupe. Concurrent callers for one key share one loader call.
  • Fail-open. An L2 or bus failure degrades, it does not throw.
  • getOrSet as the primary API. Load through getOrSet, invalidate with delete or deleteByPattern.
  • Negative caching. "This does not exist" is remembered briefly so a missing row is not a free pass to hammer the database.
  • Timeouts. softMs serves a stale value when one exists. hardMs gives up on the loader entirely.

What changes between the two shapes

Single processMultiple instances
L1Yes, the only tierYes, short TTL, primed by the bus
L2NoYes, shared, long TTL
Event busNoYes, deletes travel, loads fan out
Staleness windowL1 TTLEvent delivery time, or L1 TTL if the event is missed
sourceNot neededUnique per server, stable across restarts
broadcastSetMaxBytesNot neededRequired, no default

Where to next

On this page