7 camadas de cache em uma aplicação web moderna

Aquele Hugo
Pelando
Published in
7 min readDec 4, 2020
Cada coisa que a gente guarda e nem sabe…

Para aplicações web, cada milissegundo conta.

E com a crescente de usuários num sistema, são necessárias soluções para reduzir o uso dos recursos num servidor. Isso pode salvar a aplicação de quedas por sobrecarga ou economizar muito dinheiro em aplicações que escalam automaticamente.

Vou tentar então seguir a ordem em que as coisas acontecem na conexão entre um navegador e uma aplicação web para esclarecer quais tecnologias fazem esse papel hoje em dia e relembrar algumas que são embutidas nos navegadores há anos.

Já limpou o cache?

1. Cache DNS

Os navegadores não entendem domínios, eles entendem IPs. Antes de conseguir acessar um site como www.pelando.com.br , é necessário primeiro obter seu IP através de um servidor de DNS.

Esse servidor retorna um TTL para cada domínio, que é a informação de quanto tempo aquele domínio deve permanecer no cache. Dentro desse período, o navegador não precisa mais passar pelo servidor DNS para saber o IP do domínio acessado anteriormente, economizando 1 requisição.

Mas como nem tudo são flores, isso também significa que se o domínio passar a apontar para outra máquina dentro desse período, o usuário precisará limpar o cache do navegador para acessar o novo IP.

2. Cache de redirecionamento HTTP

Endereços mudam. Pode ser pela ideia de um domínio melhor, uma mudança no formato das URLs do site ou porque alguém quer se livrar de uma determinada URL para sempre.

Quando isso acontece, se o usuário tem o endereço antigo nos favoritos, por exemplo, não é interessante ter que passar pela URL antiga antes de ir para a nova toda vez, fazendo 2 requisições.

Para isso existe uma especificação de redirecionamento de endereços. Se a resposta de uma requisição contiver o código HTTP 301, que representa “Permanent redirect”, um cache permanente desse redirecionamento é feito.

Existe a possibilidade de especificar um tempo de duração para esse cache, mas é incomum. Se cabeçalhos de cache não forem especificados, o redirecionamento é feito sempre para o endereço armazenado, até que o usuário limpe o cache!

Sem cache, cenas como essa seriam mais comuns

3. Cache dos service workers

Nas versões mais recentes dos navegadores é possível implementar service workers. São pequenas aplicações em Javascript que, quando implementadas, são instaladas por domínio e capazes de interceptar requisições.

Os service workers podem acessar um cache reservado a eles num navegador, que normalmente é usado para guardar recursos críticos.

Uma das vantagens do cache do service worker sobre o cache padrão do navegador é a possibilidade de responder mesmo sem conexão de rede. Além disso, o desenvolvedor tem total controle sobre o cache, podendo decidir quando uma conexão ao servidor é necessária.

Obviamente, a atenção deve ser redobrada na implementação desse cache. Um problema no service worker pode impedir os usuários de visualizarem uma versão atualizada do site. Uma limpeza simples do cache não é suficiente para remover um service worker defeituoso, é necessário fazer isso com o dev tools aberto ou usar uma aba anônima.

Acontece até com os mais experientes

4. Cache HTTP

Se você ouviu alguém do suporte dizendo para você limpar o cache, era disso que ele estava falando.

Esse cache é uma cópia das requisições que o navegador faz para os recursos de um site, respeitando o tempo de expiração definido pelo servidor que entregou os recursos. Isso é útil tanto para gerar uma resposta mais rápida para o usuário quanto para diminuir a carga de um servidor.

Por exemplo, quando seu navegador baixa uma imagem, vem com ela essa informação de cache e é assim que o navegador sabe quando terá que ir ao servidor novamente para buscar a imagem ou se pode usar a versão que já está na sua máquina.

Isso serve para qualquer recurso: imagens, fontes, scripts, dados…

Se o servidor informar sobre o cache, o navegador respeitará o prazo de expiração antes de buscar na rede novamente.

O prazo desse cache varia de acordo com a implementação. Normalmente o cache para estáticos, como imagens, é mais longo, enquanto que para dados dinâmicos, como uma lista de comentários, é mais curto.

O problema acontece se o desenvolvedor quiser atualizar um desses recursos antes do tempo de expiração. Ainda mais no caso das aplicações web de hoje em dia, onde falhar em atualizar um simples script pode quebrar um site inteiro.

Para o caso dos estáticos, já faz parte dos padrões de desenvolvimento gerar nomes de arquivo com um identificador único, exatamente para evitar esse cache. Mas quando a própria página que requisita o estático permanece cacheada com os nomes de arquivos antigos, só resta limpar o cache mesmo.

5. Cache de requisições na aplicação do cliente

Foi difícil até dar um nome para isso que não confundisse com os próximos tópicos…

Com certeza essa técnica sempre existiu em menor grau no passado, mas graças à popularização de SPAs (Single Page Applications — aplicações onde não ocorre um recarregamento ao navegar entre seções), essa técnica é cada vez mais comum e agora existem até bibliotecas que a implementam de forma automática.

Uma das bibliotecas que implementa a técnica é a biblioteca do Apollo Client.

Apollo + GraphQL + React é amor ❤️ (mas às vezes dá saudade de quando o front-end era simples)

A técnica consiste em armazenar, normalmente na memória, o resultado de uma requisição a partir dos seus identificadores.

Por exemplo, se você acessa a página de um usuário com ID 1, depois navega pelo site e volta a acessar a página do mesmo usuário 1, a aplicação não vai nem deixar o navegador tentar acessar o cache.

Isto porque a aplicação possui o próprio cache, que pode ser ainda um resultado já formatado para o uso, otimizando o acesso mais uma vez.

Esse cache pode ser limpo via código e, se for em memória, também pode ser apagado simplesmente recarregando a página.

6. Cache de proxy

Já entrou num site que, mesmo após limpar o cache do navegador parecia um pouco desatualizado, mas a atualização ocorria após o login?

Isto acontece porque entre o navegador e o servidor da aplicação existe um serviço de proxy reverso.
Esse serviço, com o cache habilitado, cria uma cópia própria dos recursos do servidor.

Com isso, se 2 usuários acessam a mesma URL dentro do prazo de cache configurado, apenas o primeiro atinge a aplicação. O segundo recebe o resultado do acesso anterior, que foi armazenado em arquivo no serviço e não precisa ser processado de novo pelo servidor da aplicação.

Proxy reverso no meio para reduzir a carga do servidor de aplicação

7. Cache do banco de dados

O banco de dados, onde são persistidos os dados de uma aplicação, normalmente é armazenado em disco. Soma-se o custo das operações de busca nesse serviço, as queries, que podem demorar um tempo considerável.

Por isso, dentro da própria aplicação no servidor, acontece o cache desses dados.

Esse cache normalmente é armazenado num serviço de armazenamento de dados em memória como o Redis ou o MongoDB.

Ícone padrão de um banco de dados

Então, por exemplo, ao acessar a página de um usuário que não tenha feito uma modificação recente em seu perfil, mesmo se todos os outros caches citados até aqui tiverem expirado, a aplicação não vai demorar tanto a responder e o banco de dados não vai receber uma carga de acesso desnecessária.

Um problema comum na implementação de um cache desse tipo, porém, é um cache inconsistente. O cache de um usuário de ID 1 pode conter a lista de amigos, por exemplo. Se um dos amigos, de ID 2, excluir a conta, ele pode não sumir da lista que está no cache de ID 1.

Uma forma de evitar isso é criar um cache que tenha uma lista de referências (como uma lista de IDs dos amigos) em vez de dados completos, mas isso significa fazer mais consultas ao banco num primeiro momento para economizar consultas no futuro. A melhor alternativa varia de acordo com a natureza de cada aplicação.

Conclusão

O cache é uma solução inteligente para diversos níveis de uma aplicação, mas também possui suas complicações. É importante entender cada tipo de implementação para saber quais vantagens e desvantagens elas podem trazer e se o custo-benefício é válido para sua aplicação. Também é importante entender esses conceitos para identificar bugs relacionados à configurações feitas por terceiros.

Referências

--

--