Performance e Boas Práticas
🎯 Estratégia de TTL
Dados muito dinâmicos (mudam segundos)
const cache = new Cache({
defaultTTL: 3_000, // 3 segundos
});
await cache.fetch("temperature", getTemperature);
// Cache atualiza a cada 3s
APIs externas normais
const apiCache = new Cache({
defaultTTL: 30_000, // 30 segundos
});
await apiCache.fetch("github:repo", getGithubRepo);
// Razoável para maioria das APIsExternas
Configurações quase estáticas
const configCache = new Cache({
defaultTTL: 5 * 60_000, // 5 minutos ou mais
});
await configCache.fetch("app:config", loadConfig);
// Mudar raramente, cache longo é ok
💡 SWR para melhor UX
Quando latência importa mais que dados 100% fresh:
const dashboardCache = new Cache<Dashboard>({
defaultTTL: 10_000,
maxEntries: 1000,
});
export async function getDashboard(userId: string): Promise<Dashboard> {
return dashboardCache.fetch(
`dashboard:${userId}`,
async () => {
// Chamada lenta (200-500ms)
const data = await fetch(`/api/dashboard/${userId}`);
return data.json();
},
{
stale: true, // 👈 SWR ativa
ttl: 10_000,
}
);
}
// Timeline:
// t=0: Primeira chamada → busca API (200ms)
// t=1-10: Chamadas → retornam cache (1ms)
// t=11: TTL expirou → retorna última versão (1ms) + busca em background
// t=11.5: Cache atualizado em background, próxima chamada vai ter versão nova
Ganho prático
Sem SWR:
t=0: Fetch começa (200ms de espera)
t=200: Retorna
t=1-10: Cache (1ms)
t=11: Fetch começa novamente (200ms de espera) ← LENTO
t=211: Retorna
Com SWR:
t=0: Fetch começa (200ms em background)
t=200: Cache atualizado
t=1-10: Cache (1ms)
t=11: Retorna antigo (1ms) ← RÁPIDO
Background busca novo
t=11.5: Cache atualizado
🧠 Evitar Memory Leak
Configure maxEntries
const cache = new Cache<any>({
defaultTTL: 30_000,
maxEntries: 5_000, // Limite máximo
});
// Se exceder 5000 entradas:
// -> Remove entradas mais antigas automaticamente
Use sweepIntervalMs em processos de longa vida
const cache = new Cache<any>({
defaultTTL: 30_000,
sweepIntervalMs: 60_000, // Limpe expir ados a cada 1 minuto
});
// Sem sweep:
// Entradas expiradas ficam na memória indefinidamente
// Com sweep:
// Removidas a cada 60s, memória fico controlada
Considere maxPendingAgeMs
const cache = new Cache<any>({
maxPendingAgeMs: 30_000, // Promises pendentes "morrem" após 30s
});
// Previne que promises travadas crescam indefinidamente
Cleanup em shutdown
const cache = new Cache({
sweepIntervalMs: 10_000,
});
process.on("exit", () => {
cache.dispose(); // Param timer de sweep
console.log("Cache finalizado");
});
📊 Monitorar e Debugar
Log periódico
const cache = new Cache<any>({
defaultTTL: 30_000,
});
setInterval(() => {
const stats = cache.stats();
console.log({
hitRate: `${(stats.hits / (stats.hits + stats.misses) * 100).toFixed(1)}%`,
size: stats.size,
pending: stats.pending,
});
}, 60_000);
// Output a cada minuto:
// { hitRate: '85.5%', size: 342, pending: 2 }
Alertar se hit rate baixo
const stats = cache.stats();
const hitRate = stats.hits / (stats.hits + stats.misses);
if (hitRate < 0.50) { // < 50%
console.warn("⚠️ Cache hit rate baixo!");
console.warn("- TTL muito baixo?");
console.warn("- Chave muda demais entre chamadas?");
}
Alertar se pending muito alto
const stats = cache.stats();
if (stats.pending > 100) {
console.warn("⚠️ Muitas promises pendentes!");
console.warn("- Upstream lento?");
console.warn("- Timeout insuficiente?");
}
🚀 Otimizações avançadas
Pré-popular cache em boot
export async function initializeCache() {
const config = await fetch("/api/config").then(r => r.json());
cache.set("config", config, {
ttl: 10 * 60_000,
tags: ["config"],
});
}
app.on("start", initializeCache);
Cache warming para dados populares
const popularPostIds = ["1", "2", "3"];
for (const id of popularPostIds) {
// Pré carregue em background
getPost(id).catch(err => console.error(err));
}
Invalidação inteligente
// Ao invés de:
cache.delete(`post:1`);
cache.delete(`post:2`);
cache.delete(`post:3`);
// Use tags:
cache.invalidateTag("post"); // Remove todas de uma vez
✨ Boas práticas resumidas
- TTL apropriado → dados dinâmicos: 3-15s; normais: 30-60s; estáticos: 5min+
- SWR sempre → melhora UX em 10x para operações lentas
- Tags e prefixos → invalidação eficiente
- Monitorar stats → detectar problemas cedo
- Cleanup no shutdown → evitar memory leak
- maxEntries finito → limita memória usada
- sweepIntervalMs → remove expirados periodicamente
Seguindo essas práticas, FlowCache vai eliminar 70-90% de latência desnecessária em sua app!