2 puntos por flyingsquirrel 2026-04-17 | 4 comentarios | Compartir por WhatsApp

¿Qué es layercache?

Es una biblioteca de caché multicapa que unifica Memory → Redis → Disk bajo una sola API en Node.js.

Cuando hay un acierto de caché, obtiene el valor desde la capa más rápida y rellena automáticamente las capas superiores. Cuando hay un fallo, aunque entren 100 solicitudes simultáneas, el fetcher se ejecuta exactamente una sola vez.

¿Por qué se creó?

Al operar servicios en Node.js, la forma de ir apilando capas de caché suele seguir más o menos los mismos pasos: se empieza con una caché en memoria, cuando aumentan las instancias se añade Redis, luego aparece el problema de stampede, surgen inconsistencias de caché entre instancias... Cada problema se puede resolver por separado, pero enlazar todo esto de una sola vez a nivel de producción terminó requiriendo mucho más trabajo de lo esperado.

Por eso se creó con la idea de hacer bien ese trabajo una sola vez.

¿Cuáles son sus funciones principales?

Funcionamiento central

  • Lectura por capas + backfill automático (fallo en L1 → consulta en L2 → relleno de L1)
  • Prevención de stampede: 100 solicitudes simultáneas → 1 ejecución del fetcher
  • single-flight distribuido: elimina ejecuciones duplicadas entre instancias con bloqueo distribuido de Redis
  • Bus de invalidación L1 basado en Redis pub/sub (sincronización de caché en memoria entre instancias)

Invalidación / frescura

  • Invalidación basada en etiquetas, invalidación por comodines/prefijos
  • stale-while-revalidate, stale-if-error
  • Sliding TTL, Adaptive TTL, Refresh-ahead

Resiliencia

  • Degradación elegante: si Redis falla, omite la capa y se recupera automáticamente
  • Circuit breaker
  • Políticas de escritura strict / best-effort

Observabilidad

  • Exportador para Prometheus, hooks de OpenTelemetry
  • Medición de latencia por capa, hooks de eventos
  • CLI de administración (npx layercache stats|keys|invalidate)

Integración con frameworks
Express, Fastify, Hono, tRPC, GraphQL, Next.js

Quiero ver cifras de benchmark

Se basan en una VM de un solo núcleo + Redis real en Docker.

| Escenario | Latencia promedio |
| L1 en memoria, acierto en caliente | 0.005 ms |
| L2 Redis, acierto en caliente (1 KiB) | 0.193 ms |
| Sin caché (simulación de DB) | 5.030 ms |

  • Throughput HTTP: /layered 16,211 req/s vs /nocache 158 req/s
  • Stampede: 75 solicitudes simultáneas → 5 origin fetch (sin caché serían 375)
  • Distributed single-flight: 60 solicitudes simultáneas → 1 origin fetch

La metodología completa del benchmark y los resultados en bruto están resumidos en docs/benchmarking.md.

¿En qué se diferencia de las bibliotecas existentes?

node-cache-manager, keyv y cacheable son todas buenas opciones. Resumiendo brevemente las diferencias:

  • Prevención de stampede / Distributed single-flight: ninguna de las tres bibliotecas lo ofrece por defecto. layercache fue diseñada con estos dos puntos como núcleo.
  • Invalidación L1 entre instancias: sincroniza automáticamente la caché en memoria entre instancias con Redis pub/sub. Permite usar caché en memoria con tranquilidad en entornos multiinstancia.
  • Auto backfill: cuando una capa inferior acierta, rellena automáticamente las capas superiores.
  • Degradación elegante + Circuit breaker: el servicio sigue vivo incluso si Redis cae.

Instalación y enlaces

npm install layercache  

Si tienen preguntas sobre decisiones de diseño, especialmente sobre cómo se coordina single-flight o cómo funciona la degradación elegante, no duden en preguntar.

4 comentarios

 
sugeuljin 2026-04-18

¡Qué buena biblioteca!
¿Hay alguna razón por la que Redis esté incluido en el diseño? ¿Se asume un escenario en el que varias instancias de solo lectura se levantan al mismo tiempo? Si es así, ¿no debería el disco (local) estar ubicado en una capa anterior a Redis?

 
flyingsquirrel 2026-04-18

La razón por la que se incluye Redis es porque se asume un entorno con varios servidores. Como la memoria de cada servidor puede tener valores distintos entre sí, Redis cumple el papel de una "fuente de verdad compartida".

Que Disk vaya después de Redis se debe a que, bajo la suposición de que Redis está en la misma red local, es más rápido. Según los benchmarks, Disk tarda ~2ms y Redis ~0.02ms. Pero si Redis está lejos o la red es deficiente, el Disk local puede ser más rápido, y en ese caso lo correcto es cambiar el orden. La librería tampoco impone el orden; está diseñada para que el usuario lo defina directamente.

Sin importar dónde esté Disk, su propósito principal no es competir en velocidad, sino servir como el último seguro que sobrevive cuando tanto Memory como Redis dejan de funcionar.

 
sugeuljin 2026-04-18

Gracias por explicar la intención del diseño. jaja
Entonces, ¿quieres decir que todas las llamadas remotas se guardan como escrituras en disco local y, cuando falla una llamada remota, se hace una lectura desde el disco? Creo que también valdría la pena considerar si realmente es indispensable tener Disk en la capa de caché.

 
flyingsquirrel 2026-04-18

DiskLayer no sigue ese patrón, sino que simplemente funciona como una capa de caché normal: lee y escribe, y si falla en una capa superior, accede en orden a las siguientes. Eso pudo haber causado confusión.
El patrón que mencionas de "guardar en disco el resultado de una llamada remota y leerlo si falla" en realidad se parece más a la opción stale-if-error, pero eso se mantiene en memoria, así que se pierde cuando el proceso se reinicia.
Y sobre la observación de si DiskLayer es realmente necesario, pues... en la mayoría de los entornos con múltiples instancias, Memory → Redis suele ser suficiente, y en el momento en que Disk entra como capa, vienen también el costo de serialización y la complejidad de manejar archivos.