Phil Karlton disse que existem dois problemas difíceis na computação: naming, cache invalidation, e off-by-one errors. A piada é velha porque o problema de invalidação é real. Mas isso não significa que você deve evitar cache. Significa que precisa de uma estratégia.
Os três níveis de cache web
Uma aplicação web típica tem três pontos onde cache ajuda:
- CDN (edge cache): cache estático mais próximo do usuário. Imagens, CSS, JS bundle, páginas pré-renderizadas.
- Application cache: cache em memória ou Redis para dados que mudam pouco. Sessões, configurações, queries frequentes.
- Database cache: query results, prepared statements, materialized views.
Cada nível tem características diferentes de invalidação. CDN é o mais agressivo (TTL longo, invalidação manual). Application cache é o mais flexível (TTL curto, invalidação por evento). Database cache é o mais conservador (invalidação automática via triggers ou materialized views).
Redis: o workhorse do application cache
Redis é rápido porque mantém dados em memória. É a escolha correta para dados que precisam ser lidos milhares de vezes por segundo.
// Cache-aside pattern
async function getUser(id: string): Promise<User> {
const cacheKey = `user:${id}`
// 1. Tenta cache
const cached = await redis.get(cacheKey)
if (cached) return JSON.parse(cached)
// 2. Cache miss: busca no banco
const user = await db.user.findUnique({ where: { id } })
if (!user) throw new AppError('NOT_FOUND', 404, 'Usuário não encontrado')
// 3. Armazena no cache com TTL
await redis.setex(cacheKey, 3600, JSON.stringify(user)) // 1 hora
return user
}
O padrão cache-aside é o mais comum: o código tenta cache primeiro, busca no banco se miss, e armazena no cache para próxima leitura. É simples e funciona para a maioria dos casos.
Invalidation strategies
Os padrões de invalidação que funcionam em produção:
TTL (Time-To-Live)
O cache expira automaticamente após um período. Simples, previsível, aceita dados eventualmente consistentes.
// Dados que mudam raramente: TTL longo
await redis.setex('config:app', 86400, JSON.stringify(config)) // 24h
// Dados que mudam frequentemente: TTL curto
await redis.setex('feed:popular', 300, JSON.stringify(feed)) // 5min
Event-driven invalidation
Quando o dado muda, invalida o cache explicitamente. Requer um evento de domínio que dispara a invalidação.
// Ao atualizar usuário, invalida o cache
async function updateUser(id: string, data: UpdateUserDto): Promise<User> {
const user = await db.user.update({ where: { id }, data: data })
// Invalida cache do usuário e listas que o incluem
await redis.del(`user:${id}`)
await redis.del('users:active') // invalida cache da listagem
return user
}
Write-through cache
Atualiza cache e banco na mesma operação. Garante consistência imediata, mas adiciona latência na escrita.
CDN cache: headers que importam
Controlar cache de CDN requer os headers corretos:
// Cache estático: 1 ano, immutable
Cache-Control: public, max-age=31536000, immutable
// API com dados que mudam
Cache-Control: private, max-age=0, must-revalidate
ETag: "abc123"
// Página que muda por dia
Cache-Control: public, max-age=3600, stale-while-revalidate=86400
stale-while-revalidate permite servir conteúdo stale enquanto revalida em background. O usuário recebe resposta rápida, e o conteúdo é atualizado sem delay perceptível.
Cache warming
Após deploy ou restart, caches ficam vazios. Isso causa um pico de latência (cold start). Cache warming pré-carrega dados frequentes antes que o tráfego chegue:
// No startup do servidor
async function warmCache() {
const popularProducts = await db.product.findMany({
orderBy: { views: 'desc' },
take: 100
})
await Promise.all(popularProducts.map(p =>
redis.setex(`product:${p.id}`, 7200, JSON.stringify(p))
))
}
Quando não cachear
Nem tudo precisa de cache. Dados que mudam a cada requisição (saldos em tempo real), dados sensíveis sem controle de acesso adequado, e resultados de queries simples e rápidas não se beneficiam de cache. O overhead de invalidar e manter o cache pode ser maior que o ganho de performance.
Curtiu o conteúdo?
Construo produtos web e soluções com IA do jeito certo — arquitetura sólida, código sustentável e entrega real.
Vamos conversar