2 pontos por flyingsquirrel 2026-04-17 | 4 comentários | Compartilhar no WhatsApp

O que é o layercache?

É uma biblioteca de cache em múltiplas camadas para Node.js que reúne Memory → Redis → Disk em uma única API.

Quando há cache hit, o valor é buscado na camada mais rápida e as camadas superiores são preenchidas automaticamente. Em caso de miss, mesmo que cheguem 100 requisições simultâneas, o fetcher é executado apenas uma vez.

Por que foi criado?

Ao operar serviços em Node.js, a forma de empilhar camadas de cache costuma seguir um roteiro bem parecido. Você começa com cache em memória, depois conecta o Redis quando o número de instâncias aumenta, aí encontra problemas de stampede, surgem inconsistências de cache entre instâncias... Cada ponto pode ser resolvido separadamente, mas juntar tudo isso de forma pronta para produção deu mais trabalho do que parecia.

Por isso, a ideia foi fazer esse trabalho direito uma única vez.

Quais são os principais recursos?

Comportamento principal

  • Leitura em camadas + backfill automático (miss em L1 → consulta em L2 → preenchimento de L1)
  • Prevenção de stampede: 100 requisições simultâneas → 1 execução do fetcher
  • Distributed single-flight: elimina execuções duplicadas entre instâncias com lock distribuído via Redis
  • Barramento de invalidação de L1 baseado em Redis pub/sub (sincronização do cache em memória entre instâncias)

Invalidação / atualidade

  • Invalidação baseada em tags, invalidação por wildcard/prefixo
  • Stale-while-revalidate, Stale-if-error
  • Sliding TTL, Adaptive TTL, Refresh-ahead

Resiliência

  • Graceful degradation: em falhas do Redis, pula a camada e se recupera automaticamente
  • Circuit breaker
  • Políticas de escrita strict / best-effort

Observabilidade

  • Exportador para Prometheus, hooks de OpenTelemetry
  • Medição de latência por camada, hooks de eventos
  • Admin CLI (npx layercache stats|keys|invalidate)

Integração com frameworks
Express, Fastify, Hono, tRPC, GraphQL, Next.js

Quero ver os números de benchmark

Os testes foram feitos com base em uma VM de núcleo único + Redis real em Docker.

| Cenário | Latência média |
| L1 memory warm hit | 0.005 ms |
| L2 Redis warm hit (1 KiB) | 0.193 ms |
| Sem cache (simulação de DB) | 5.030 ms |

  • Throughput HTTP: /layered 16,211 req/s vs /nocache 158 req/s
  • Stampede: 75 requisições simultâneas → 5 buscas na origem (sem cache seriam 375)
  • Distributed single-flight: 60 requisições simultâneas → 1 busca na origem

A metodologia completa de benchmark e os resultados brutos estão em docs/benchmarking.md.

Em que ele difere das bibliotecas existentes?

node-cache-manager, keyv e cacheable são todas boas opções. Resumindo as diferenças:

  • Prevenção de stampede / Distributed single-flight: nenhuma das três bibliotecas oferece isso por padrão. O layercache foi projetado tendo esses dois pontos como núcleo.
  • Invalidação de L1 entre instâncias: sincroniza automaticamente o cache em memória entre instâncias usando Redis pub/sub. Isso permite usar cache em memória com segurança em ambientes com múltiplas instâncias.
  • Backfill automático: quando há hit em uma camada inferior, as camadas superiores são preenchidas automaticamente.
  • Graceful degradation + Circuit breaker: o serviço continua vivo mesmo se o Redis cair.

Instalação e links

npm install layercache  

Se você tiver dúvidas sobre as decisões de design, especialmente sobre a forma de coordenação do single-flight ou sobre o funcionamento do graceful degradation, fique à vontade para perguntar.

4 comentários

 
sugeuljin 2026-04-18

Boa biblioteca!
Existe algum motivo para o Redis ter sido incluído no design? Você está assumindo um cenário em que várias instâncias somente de leitura sobem ao mesmo tempo? Se for esse o caso, o disco (local) não deveria ficar em uma camada à frente do Redis?

 
flyingsquirrel 2026-04-18

O Redis entra no cenário assumindo que há vários servidores. Como a memória de cada servidor pode ter valores diferentes, o Redis atua como a "fonte compartilhada da verdade".

O Disk vem depois do Redis porque, assumindo que o Redis está na mesma rede local, ele é mais rápido. Pelos benchmarks, o Disk fica em ~2ms e o Redis em ~0.02ms. Mas, se o Redis estiver longe ou a rede estiver ruim, o Disk local pode ser mais rápido; nesse caso, faz sentido inverter a ordem. A biblioteca também não força essa ordem, e a estrutura permite que o próprio usuário a defina.

Independentemente de onde esteja, o principal objetivo do Disk não é competir em velocidade, mas servir como a última camada de segurança quando Memory e Redis falham completamente.

 
sugeuljin 2026-04-18

Obrigado pela explicação da intenção do design.
Então você quer dizer que todas as chamadas remotas são salvas como gravações no disco local e, quando a chamada remota falha, é feita uma leitura do disco, certo? Talvez também valha a pena refletir se a camada de cache realmente precisa de Disk.

 
flyingsquirrel 2026-04-18

O DiskLayer não segue esse padrão; ele simplesmente funciona como uma camada de cache comum — tanto lê quanto escreve, e a estrutura acessa as camadas em sequência quando há miss na camada superior. Isso pode ter causado confusão.
O padrão que você mencionou, de "salvar em disco o resultado de uma chamada remota e depois ler em caso de falha", na verdade fica mais próximo da opção stale-if-error, mas nesse caso os dados ficam em memória, então se o processo reiniciar eles se perdem.
E quanto ao ponto de se o DiskLayer é realmente necessário: bem, na maioria dos ambientes com múltiplas instâncias, Memory → Redis já costuma ser suficiente, e no momento em que o disco entra como camada, vêm junto o custo de serialização e a complexidade de gerenciar arquivos.