Nginx: Atualizando o cache de forma assíncrona

proxy_cache_background_update e RFC 5861

Ricardo Baltazar
Editora Globo
3 min readAug 22, 2018

--

Introdução

Muitas vezes o seu site / API depende de acesso a API de terceiros. Ter que fazer uma requisição a um terceiro toda vez que você tiver que exibir uma informação é extremamente custoso, porém muitas vezes é necessário porque essa informação completa os dados que você precisa entregar.

É impossível você prever quando esse terceiro terá problema como um deploy errado ou até mesmo estar sofrendo um ataque DDOS.

Uma solução para esse problema pode ser servir um conteúdo obsoleto (stale) enquanto o seu cache é atualizado assincronamente. Veja mais na RFC 5861.

Para isso vamos usar a diretiva do Nginx chamada proxy_cache_background_update.

Demonstração

O projeto com o código desse artigo pode ser clonado aqui.

Eu criei um API em flask que reponde o datetime.now() depois de um sleep() de três segundos. Isso porque eu quero uma rota lenta para que fique fácil de ver o cache sendo atualizado.

Ela adiciona um Cache-Control=max-age=10 para podemos ver o HIT acontecendo.

O ambiente vai subir com um docker-compose up onde vamos subir um nginx já configurado para acessar e repassar os requests para a API.

Esse é o compose usado:

Ao executar você deve ver alguma coisa assim:

Subindo compose

Não me apeguei a nenhuma boa prática para criar a API e muito menos para configurar o Nginx. Não usem esse código em produção sem saberem o que estão fazendo.

Para destruir todo o cache e testar novamente basta fazer um docker-compose down e subir novamente.

Depois que o docker subir os conteiners, acesse localhost:5000 e localhost:83. Toda vez que você acessar pela porta 5000 você vai bater diretamente na API e sempre vai demorar três segundos para ver a resposta. O que não vai acontecer com a porta 83 que vai bater no Nginx.

A primeira vez que você bater na porta 83 você vai esperar três segundos pela resposta, e vai ganhar umX-Cache-Status: MISS porque não existe um cache ativo para a rota.

Após a primeira resposta, você vai ficar ganhando X-Cache-Status: HIT durante dez segundo porque sua API retornou um header com Cache-Control=max-age=10 , lembra?

Após dez segundos você vai ganhar um X-Cache-Status: STALE e nesse momento o Nginx vai fazer uma requisição para a sua API.

Então o que aconteceu, um cliente bateu na rua rota, ganhou uma resposta stale e seguiu a vida, mas o seu Nginx ao mesmo tempo pediu a API para atualizar o cache. Quando a API retornar para o Nginx ele vai atualizar o cache e vai voltar a retornar HIT .

OK! Mas e se outro cliente acessar a url depois que o Nginx retornou o stale e antes da API atualizar o cache?

O tempo entre o stale e a o retorno da API é chamado de update e durante todo esse tempo qualquer requisição vai receber X-Cache-Status: UPDATING .

Preparei um gif para tentar demonstrar, desculpa o amadorismo.

Demonstração para o que foi dito acima

Conclusão

A diretiva proxy_cache_background_update do Nginx pode nos dar muito poder ao permitir servir conteúdo stale enquanto o Nginx faz a atualização do cache assincronamente.

Não resolve todos os problemas de resiliência, porque se existirem muitos acessos a um cache inexistente a sua API pode ser comprometida até o cache subir.

Espero ter colocado mais uma opção entre as que vocês conhecem para preparar um ambiente de produção.

Qualquer dúvida, sugestão e/ou crítica não deixem de falar!

--

--