Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Maurício Antunes
Maurício Antunes

Posted on

     

Quem mexeu no meu cache?

O objetivo desse texto é mostrar como um erro de configuração no cache da sua aplicação pode trazer problemas sérios que vão desde uma dor de cabeça até a perda de dinheiro.

O que é caching?

Caching é uma estratégia utilizada para obter dados de forma mais rápida em uma aplicação. Nossa memória recente é uma espécie de cache, pois não precisamos fazer um grande esforço mental pra lembrar.

Se você está pensando em usar alguma forma de cache na sua aplicação, provavelmente você está com alguma parte da sua aplicação executando devagar, por exemplo. Cache pode ajudar sua aplicação de diversas maneiras, até como mecanismo de resiliência.

Errando na configuração do cache

É possível degradar de forma severa uma aplicação ao configurar o cache de maneira errada. Alguns tipos de aplicação são extremamente dependentes de cache, como serviços de storage e multimídia, como transmissão de áudio e vídeo pela internet.

Para o exemplo deste artigo, vamos usar exemplos usando Ruby e um servidor web famoso, o NGINX.

Vamos falar de dois tipos de ataque que podem acontecer nos seus servidores.

Minha aplicação caiu porque o cache não funcionou!

Cache que não funciona é muito comum, principalmente quando sites usam CDNs externas e as pessoas envolvidas não têm conhecimento de como um erro de configuração pode derrubar a aplicação.

Vamos supor que você seja dev de um site que contenha várias imagens de produtos, com boa resolução e você adicione caching para que o seu serviço de imagens não seja acionado toda requisição. Essa é uma ótima ideia e há expectativas de que ela vai melhorar a performance da aplicação. Porém, a configuração que você fez coloca os parâmetros da query string como parte da chave de cache.

Chave de cache(cache key) é um hash formado a partir de variáveis especificadas pela configuração das diretivas do servidor. Essa chave é uma maneira de mapear um valor para um hash md5 que vai ser alocado em um grande mapa, de rápido acesso, no seu servidor

O que pode dar errado? Essa imagem abaixo exemplifica requests que furam o cache ao usar parâmetros quaisquer na query string.

dois fluxos de request mostrando o erro ao usar a query string como chave de cache

Podemos representar isso com código Ruby, criando funções que definem as chaves de cache, similares às configurações que podemos fazer no NGINX:

require'digest'# Esse é o nosso cachecache={}# Uma lista de requests a serem feitosreqs=['/airfy.jpg','/airfy.jpg','/airfy.jpg',]# Nossa função que define a cache key de acordo com# o valor passadodefgood_cache_key_hash(value)# Divide a string por "?" e remove o "/"key=value.split('?')[0].slice(1,value.size)Digest::MD5.hexdigestkeyend# Valida que todos os paths vão usar a mesma chave de cacheputs(reqs.map{|req|good_cache_key_hash(req)})# Agora vamos ter uma nova lista de requests maliciosos (ou não)reqs=['/airfy.jpg?foo=1','/airfy.jpg?foo=2','/airfy.jpg?foo=3',]# Nossa função que define a cache key de acordo com# o valor passado mas dessa vez ela não foi pensada corretamentedefbad_cache_key_hash(value)key=valueDigest::MD5.hexdigestkeyend# Valida que todos os paths vão usar chaves diferentes,# mesmo que as imagens sejam exatamente as mesmasputs(reqs.map{|req|bad_cache_key_hash(req)})
Enter fullscreen modeExit fullscreen mode

O resultado fica como a seguir:

43eb6cb76f885b82fbfef92cbee4acd143eb6cb76f885b82fbfef92cbee4acd143eb6cb76f885b82fbfef92cbee4acd19a03b0f4730f70964c01f97355823f20b0affdcb74f872918bb795937f98feaa0082e52c9b5e1613888e1a26bbc96f37
Enter fullscreen modeExit fullscreen mode

Observe que os3 primeiros valores são iguais, provando que a chave será a mesma. Entretanto, ospróximos três requests estão com os valores diferentes, mesmo que a imagem seja a mesma.

O que podemos concluir? Que nosso cache vai ficar furado por uma má definição do hash computado que é usado para mapear requests aos nossos valores.

Às vezes vejo uma air fry, às vezes um liquidificador

Você já entrou em um site que a imagem ficava trocando, sem ter um motivo muito óbvio? Provavelmente muitos objetos estão apontando pra mesma cache key.

cache key apontando sempre para o mesmo objeto

Esse caso acima está relacionado a um erro de configuração que vai fazer todas as URLs de imagem do seu site apontarem pro mesmo arquivo. Como?

Lembra que a cache key é o hash md5 que aponta qual lugar do mapa de objetos vai ser consultado? Então, nesse caso a cache key apontou qualquer URL para o mesmo lugar. A primeira requisição trouxeairfry.jpg e fez cache. As requisições subsequentes vão sempre trazer a imagem da airfry, até que o tempo de cache termine e faça a imagemexpirar.

O exemplo abaixo um Ruby mostra que uma chave de cache mal definida pode fazer todos os objetos apontarem pro mesmo hash:

# Esse é o nosso cachecache={}# Agora vamos supor que nós deixamos passar que nossa cache key# foi definida usando a extensão da imagem. Isso é totalmente# possível quando você usa variáveis erradas ou define# uma função de cache e não testa ela corretamentedefwrong_cache_key(value)# pega a extensão do caminho passadokey=value.split(".").lastDigest::MD5.hexdigestkeyend# Uma lista de requests a serem feitosreqs=['/airfy.jpg','/liquidificador.jpg','/airfy.jpg',]# Valida que todos as chaves são a mesmaputs(reqs.map{|req|wrong_cache_key(req)})
Enter fullscreen modeExit fullscreen mode

O resultado fica como a seguir:

c36bbd258b7ee694eb987221b2b197b0c36bbd258b7ee694eb987221b2b197b0c36bbd258b7ee694eb987221b2b197b0
Enter fullscreen modeExit fullscreen mode

Testando as requisições em um servidor web

Foi criado o projetohttps://github.com/mauricioabreu/my-cache-haz-a-problem que contém o código do web server e os exemplos didáticos criados para o texto.

Cache key com query string

Aqui demonstramos o caso em que usar a query string como cache pode degradar sua aplicação:

curl -v "http://localhost:8080/items/airfry.jpg?foo=1"< HTTP/1.1 200 OK< Content-Length: 101321< X-Cache-Status: MISS
Enter fullscreen modeExit fullscreen mode

Uma boa estratégia de cache ignoraria esses parâmetros na chave de cache. Um segundo request com os parâmetrosfoo=2 deveria retornarHIT

HIT - objeto encontrado no cache
MISS - objeto não encontrado no cache. Uma requisição ao upstream será realizada, o objeto será baixado e cacheado.

curl -v "http://localhost:8080/items/airfry.jpg?foo=2"< HTTP/1.1 200 OK< Content-Length: 101321< X-Cache-Status: MISS
Enter fullscreen modeExit fullscreen mode

Imagine essa informação nas mãos erradas?

Não é um erro formar a cache key com parâmetros da query string. Um exemplo bom é se o seu site renderiza imagens com tamanhos predefinidos, como/products/airfry.jpg?width=300&height=250. Esses parâmetros podem ser usados e vão aumentar a performance do seu cache.

Cache key errada

Usar a cache key errada pode deixar as pessoas usuárias do seu site infelizes ao receber sempre a mesma imagem:

curl -v http://localhost:8080/products/airfry.jpg< HTTP/1.1 200 OK< Content-Length: 101321< X-Cache-Status: MISS
Enter fullscreen modeExit fullscreen mode

O primeiro request está OK. O estado do cache éMISS, ainda não existia no cache e foi resgatado do nossostorage.

Vamos requisitar a imagem de um liquidificador.

curl -v http://localhost:8080/products/liquidificador.jpg< HTTP/1.1 200 OK< Content-Length: 101321< X-Cache-Status: HIT
Enter fullscreen modeExit fullscreen mode

Aqui observamos duas coisas estranhas: otamanho e oX-Cache-Status. Muita coincidência o tamanho da imagemliquidificador ter o exato mesmo tamanho da imagem daair fry, não é? Além disso, como uma imagem que nunca requisitamos trouxe um estado deHIT? Isso aconteceu porque a chave de cache foi baseada, erroneamente, na extensão da requisição,jpg

Solucionando os problemas

Uma das maneiras mais simples de resolver os problemas apresentados é usar a cache key padrão do NGINX, que é$scheme$proxy_host$request_uri. Em detalhe:

  • Scheme - HTTP/HTTPS
  • Proxy host - host e porta do endereço do storage (ou do upstream usado)
  • Request URI - URI original com os parâmetros da query string

Algumas dicas:

  • Use a cache key default quando puder;
  • Verifique suas cache keys antes de colocar seu site em produção;
  • Colete métricas de uso do cache. Quanto mais requests comHIT, melhor vai ser seucache hit ratio.

Evil icons created by Smashicons - Flaticon

Top comments(2)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
josethz00 profile image
José Thomaz
Software Developer, working as full stack developer and studying about IOT, AI and compilers in the free time. Always trying to help people!
  • Location
    São Paulo, Brazil
  • Education
    Computer systems analysis
  • Joined

muito bom

CollapseExpand
 
rafaelborsani profile image
Rafael Borsani
  • Joined
• Edited on• Edited

Parabéns Maurício, texto com dicas e informações muito boas!!!! Além da didática muito boa que dispensa comentários.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I enjoy coding, video streaming and programming languages
  • Location
    Brazil
  • Work
    Software Developer @Globo
  • Joined

More fromMaurício Antunes

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp