Meu CMS gera páginas bastante complexas e, portanto, demora um pouco para fazê-lo (cerca de 2 segundos), o que está bem acima do meu orçamento de tempo para fornecer páginas ao cliente.
Porém é muito barato para mim saber a versão atual de uma determinada página e é muito fácil para mim dizer se uma determinada versão ainda está atualizada. Como tal, eu gostaria de poder colocar uma estratégia baseada em ETag onde cada solicitação para uma página precise ser revalidada, mas o servidor responderá em no máximo 10 ms se o conteúdo não mudar.
Para que isso seja eficaz, preciso compartilhar esse cache entre todos os meus clientes. Enquanto a ETag for revalidada, todas as minhas páginas permanecerão idênticas para todos os usuários, para que eu possa compartilhar seu conteúdo com segurança.
Para fazer isso, minha página emite um:
Cache-Control: public, no-cache, must-revalidate
ETag: W/"xxx"
Ao testar em um navegador, funciona muito bem: a página permanece no cache e simplesmente é revalidada no servidor toda vez que atualizo a página, obtendo 304 na maioria das vezes ou 200 quando altero a versão do conteúdo.
Tudo que preciso agora é compartilhar esse cache entre clientes. Essencialmente:
- Fase A
- O cliente A envia uma solicitação ao proxy
- O proxy não tem cache, então pergunta ao back-end
- Backend responde 200 com uma ETag
- O proxy responde 200 com uma ETag
- Fase B
- O cliente B envia a mesma solicitação ao proxy
- O proxy está em cache, mas deve revalidar (porque não há cache, deve-revalidar e ETag)
- O back-end responde com 304 (porque a solicitação de revalidação inclui o cabeçalho If-None-Match com a ETag armazenada em cache)
- Proxy responde 200 com um Etag
- Fase C
- O cliente A envia a mesma solicitação novamente, desta vez com If-None-Match
- O proxy solicita ao back-end o cabeçalho If-None-Match fornecido (não o armazenado em cache)
- O servidor back-end responde 304
- O proxy responde 304
Eu tentei o nginx, mas requer muitos ajustes para fazê-lo funcionar remotamente. Então experimentei o Traefik antes de perceber que o middleware de cache faz parte da versão corporativa. Então percebi que o Varnish parece implementar o que eu quero.
Então aqui vou eu com minha configuração do Varnish:
vcl 4.0;
backend default {
.host = "localhost";
.port = "3000";
}
backend api {
.host = "localhost";
.port = "8000";
}
sub vcl_recv {
if (req.url ~ "^/back/" || req.url ~ "^/_/") {
set req.backend_hint = api;
} else {
set req.backend_hint = default;
}
}
E claro... Não funcionou.
Ao variar os Cache-Control
cabeçalhos, obtenho o resultado de um cache compartilhado, mas que não é revalidado, ou apenas uma passagem para o cliente, mas nunca parece manter o conteúdo em cache como gostaria.
O que estou faltando para implementar essa lógica de revalidação de cache/ETag compartilhada? Suponho que estou perdendo algo óbvio, mas não consigo descobrir o quê.