Cache é uma das ferramentas mais sedutoras da engenharia de software. Com pouco esforço, você transforma uma operação lenta em uma resposta instantânea. O sistema fica mais rápido, a infraestrutura respira, o usuário sorri. Parece mágica.
E é justamente por parecer mágica que cache causa tanto estrago. Ele resolve o problema de performance de forma tão fácil que as pessoas saem espalhando cache por todo lado, sem perceber que estão trocando um problema visível, lentidão, por um problema invisível, dados errados. E dado errado é muito pior que dado lento.
Este é um guia rápido para usar cache bem. A ideia não é esgotar o assunto, e sim dar a você os critérios que separam o cache que ajuda do cache que vira armadilha. Como diz a piada clássica da computação, só existem dois problemas difíceis: invalidação de cache, nomear coisas e erro de mais um.
O que cache faz, em uma frase
Cache é guardar o resultado de uma operação cara para reutilizá-lo, em vez de refazer a operação toda vez. Você calcula uma vez, guarda, e nas próximas vezes entrega a resposta guardada.
A operação "cara" pode ser uma consulta pesada ao banco, uma chamada a um serviço externo, um cálculo complexo ou a renderização de uma página. O ganho vem de não repetir trabalho. Quando o mesmo resultado é pedido muitas vezes e muda pouco, cache é quase sempre uma boa ideia.
A palavra-chave é "muda pouco". É aí que mora toda a complexidade, e é aí que a maioria erra.
Boa prática: cacheie o que é lido muito e muda pouco
O candidato ideal a cache tem dois traços: é acessado com frequência e muda raramente. Pense numa lista de categorias de produtos, na configuração de um sistema, no perfil público de um usuário. Esse tipo de dado é lido o tempo todo e atualizado de vez em quando, cache perfeito.
O candidato ruim a cache é o oposto: dado que muda a cada instante ou cuja exatidão é crítica em tempo real. O saldo de uma conta bancária, o estoque disponível no momento da compra, o preço numa negociação ativa, cachear isso pode entregar ao usuário um número que já não é verdade, com consequências reais.
A pergunta prática antes de cachear qualquer coisa: o que acontece se o usuário ver um valor alguns segundos ou minutos desatualizado? Se a resposta é "nada demais", cache. Se a resposta é "um problema sério", pense duas vezes.
O problema de verdade: invalidação
Colocar dado no cache é trivial. O difícil é saber quando tirar, ou atualizar. Esse é o problema da invalidação, e é onde quase todo bug de cache nasce.
Existem duas estratégias básicas, e ambas têm seu lugar. A primeira é expiração por tempo: o dado vive no cache por um período definido e depois é descartado. Simples, robusto e suficiente para a maioria dos casos. Você aceita que o dado pode ficar desatualizado por, digamos, cinco minutos, e segue a vida.
A segunda é invalidação por evento: quando o dado muda, você ativamente remove ou atualiza a versão em cache. É mais preciso, porém mais frágil, exige que toda alteração do dado lembre de avisar o cache, e basta um caminho esquecido para o usuário ver informação velha indefinidamente.
A boa prática para um guia rápido: prefira expiração por tempo sempre que a tolerância permitir. É mais simples, mais resistente a erro humano, e evita a classe de bug em que o cache "esquece" de atualizar. Reserve a invalidação por evento para quando a precisão realmente justificar a complexidade.
Boa prática: defina o que acontece quando o cache falha
Cache é uma camada extra, e camadas extras falham. O servidor de cache pode cair, ficar indisponível ou lento. A pergunta que muita gente esquece de responder: e aí, o sistema para?
Cache bem-feito é uma otimização, não uma dependência. Se o cache sumir, a aplicação deve continuar funcionando, mais lenta, talvez, mas funcionando, buscando os dados na fonte original. Quando o sistema quebra inteiro porque o cache caiu, você não tem uma otimização; tem um ponto único de falha disfarçado de melhoria de performance.
Há ainda um detalhe traiçoeiro: quando o cache esvazia de uma vez, todas as requisições batem na fonte original ao mesmo tempo, e a sobrecarga pode derrubar justamente o que o cache protegia. É um efeito conhecido, e vale desenhar a recuperação pensando nele, para que o sistema reaqueça o cache sem se afogar.
Reflexão crítica: cache que esconde o problema errado
Há um uso de cache que é tecnicamente correto e estrategicamente preguiçoso: cachear para esconder uma consulta mal feita. A consulta é lenta porque está mal escrita ou o banco está mal modelado, e em vez de corrigir a causa, joga-se cache por cima. Funciona, até o cache expirar, até o caso não cacheável aparecer, até o problema mudar de lugar.
Cache deveria acelerar o que já é eficiente, não maquiar o que é ineficiente. Quando você se pega usando cache para tornar suportável algo que deveria ser consertado, vale parar e olhar a causa. O cache, nesse caso, está adiando uma dívida, não pagando.
Há também o custo cognitivo. Cada camada de cache é mais uma coisa em que o dado pode estar desatualizado, mais um lugar para investigar quando algo está estranho. "Por que esse usuário está vendo informação antiga?" é uma das perguntas mais frustrantes de depurar, justamente porque o cache é invisível até dar problema. Cache demais transforma um sistema simples em um quebra-cabeça de versões. Use com parcimônia, documente onde está, e prefira menos camadas bem-entendidas a muitas camadas misteriosas.
O que fica
Cache é uma ferramenta poderosa e mal-empregada com a mesma frequência. Bem usado, deixa sistemas rápidos e baratos. Mal usado, entrega dado errado, esconde problemas reais e cria bugs difíceis de rastrear.
As boas práticas cabem em poucas linhas: cacheie o que é lido muito e muda pouco; prefira expiração por tempo à invalidação manual; garanta que o sistema sobrevive sem o cache; e nunca use cache para esconder um problema que deveria ser corrigido. O resto é ajuste fino.
Se você está enfrentando problemas de performance e cogitando cache como solução, vale primeiro entender se o gargalo é mesmo de leitura repetida ou de algo mais profundo. No blog há outros textos sobre backend, performance e arquitetura que aprofundam essas escolhas.
Leia também
- Backend para aplicativos: boas práticas para times pequenos que não podem errar
- Cache em Aplicacoes: Boas Praticas e Fundamentos
- Cache em Aplicacoes: Boas Praticas e Passos Essenciais
- Cache Em Aplicacoes
- Arquitetura de Aplicativos: Guia Completo para Sistemas Escalaveis
- Escalabilidade de Aplicações: Guia Técnico Completo