Upgrade to Pro — share decks privately, control downloads, hide ads and more …

50 tons de cache – estratégias de cache do HTTP ao memecached

50 tons de cache – estratégias de cache do HTTP ao memecached

Guilherme Cavalcanti

April 27, 2016
Tweet

More Decks by Guilherme Cavalcanti

Other Decks in Technology

Transcript

  1. MAS PRA QUÊ? •Quanto mais perto do navegador a resposta

    estiver, melhor; •Geralmente armazenar é mais barato do que construir; •A internet é IO bound Porque É Necessário Fazer Cache Das Respostas?
  2. O QUE É? •Controlado utilizando cabeçalhos do HTTP •Navegador mantém,

    localmente, cópias do conteúdo •Mecanismos para invalidação •Padrão •Simples
  3. BASEADO EM TEMPO •O servidor sempre envia a data da

    última modificação •O browser mantém uma cópia local •Para cada requisição feita para o servidor, o browser informa a data da cópia local •Se o servidor não possuir uma versão mais recente, retorna 304 (not modified) Quando Os Recursos São Simples. Sem Aninhamento Ou Agregações;
  4. BASEADA EM CONTEÚDO •Servidor sempre gera um hash para o

    conteúdo •O browser mantém uma cópia local e o hash •Para cada requisição feita para o servidor, o browser informa o hash da cópia local •Se o servidor não possuir um hash diferente do conteúdo, retorna not modified (304) Quando Os Recursos São Mais Complexos; Aninhamento E Agregação;
  5. • Controller para gráficos • Muitos dados no DB CONEXTO:

    CONTROLLER 1 class ChartsController < ApplicationController 2 def index 3 @data = DataPoint.all 4 end 5 end
  6. • jbuilder • Poderia ser um representer • Array de

    arrays • Muitos dados no JSON CONTEXTO: VIEW 1 json.array! @data do |point| 2 json.array! [point.date, point.value] 3 end
  7. • jbuilder • Poderia ser um representer • Array de

    arrays • Muitos dados no JSON CONTEXTO: JSON 1 $ curl localhost:9292/charts | json_pp 5 [ 6 [ 7 "2016-03-09T04:00:00.000Z", 8 0.950324529182683 9 ], 10 [ 11 "2016-03-09T04:00:30.000Z", 12 0.933245508699859 13 ], 14 [ 15 "2016-03-09T04:01:00.000Z", 16 0.0724712770762144 17 ], 18 [ 19 "2016-03-09T04:01:30.000Z", 20 0.558231590926938 21 ],
  8. • Condicional (3) • Última modificação (3) • Consulta no

    banco (4) CONTROLLER 1 class ChartsController < ApplicationController 2 def index 3 if stale?(last_modified: last, etag: nil) 4 @data = DataPoint.all 5 end 6 end 7 8 def last 9 Time.new(2016, 02, 10, 11, 22) 10 end 11 end
  9. REQUISIÇÃO 1 $ curl localhost:9292/charts -vv -o /dev/null 2 >

    GET /charts HTTP/1.1 3 > Host: localhost:9292 4 > User-Agent: curl/7.43.0 5 > Accept: */*
  10. • Status 200 (7) • Resposta com conteúdo • Last-Modified

    (11) • Browser guarda a data e o conteúdo RESPOSTA 7 < HTTP/1.1 200 OK 8 < X-Frame-Options: SAMEORIGIN 9 < X-XSS-Protection: 1; mode=block 10 < X-Content-Type-Options: nosniff 11 < Last-Modified: Wed, 10 Feb 2016 14:22:00 GMT 12 < Content-Type: application/json; charset=utf-8
  11. • If-Modified-Since (20) • Data da última cópia local PRÓXIMAS

    REQUISIÇÕES 20 $ curl localhost:9292/charts \ -H 'If-Modified-Since: Thu, 10 Mar 2016 14:22:00 21 > GET /charts HTTP/1.1 22 > Host: localhost:9292 23 > User-Agent: curl/7.43.0 24 > Accept: */* 25 > If-Modified-Since: Thu, 10 Mar 2016 14:22:00 GMT
  12. • 304 – not modified (27) • Sem conteúdo RESPOSTA

    VAZIA 27 < HTTP/1.1 304 Not Modified 31 < Last-Modified: Wed, 10 Feb 2016 14:22:00 GMT
  13. • SELECT e View na primeira requisição • Sem SELECT

    na segunda • Sem view na segunda • 123x mais rápido! O QUE ISSO SIGNIFICA? 19 Started GET "/charts" for 127.0.0.1 at 2016-03-10 11:24:41 -0300 20 Processing by ChartsController#index as */* 21 DataPoint Load (18.6ms) SELECT "data_points".* FROM "data_points" 22 Rendered charts/index.json.jbuilder (234.3ms) 23 Completed 200 OK in 247ms (Views: 224.8ms | ActiveRecord: 19.1ms) 44 Started GET "/charts" for 127.0.0.1 at 2016-03-10 11:25:28 -0300 45 Processing by ChartsController#index as */* 46 Completed 304 Not Modified in 2ms (ActiveRecord: 0.0ms)
  14. • Controller para gráficos • Muitos dados no DB CONTROLLER

    1 class ChartsController < ApplicationController 2 def index 3 @data = DataPoint.all 4 end 5 end
  15. • Condicional (3) • Hash gerado a partir do etag

    (3) • É menos custoso contar do que pegar os registros CONTROLLER 1 class ChartsController < ApplicationController 2 def index 3 if stale?(last: nil, etag: DataPoint.count) 4 @data = DataPoint.all 5 end 6 end 7 end
  16. REQUISIÇÃO 1 $ curl localhost:9292/charts -vv -o /dev/null 2 >

    GET /charts HTTP/1.1 3 > Host: localhost:9292 4 > User-Agent: curl/7.43.0 5 > Accept: */*
  17. • MD5 do valor passado para o argumento etag (71)

    • Resposta com conteúdo • Browser guarda conteúdo e etag RESPOSTA 67 < HTTP/1.1 200 OK 71 < ETag: "d83b123020456902d309159915cf2cad"
  18. • If-None-Match (88) • Navegador envia identificador da cópia local

    PRÓXIMAS REQUISIÇÕES 88 $ curl localhost:9292/charts -vv -o /dev/null
 -H 'If-None-Match: d83b123020456902d309159915cf2cad' 89 > GET /charts HTTP/1.1 90 > Host: localhost:9292 91 > User-Agent: curl/7.43.0 92 > Accept: */* 93 > If-None-Match: d83b123020456902d309159915cf2cad
  19. • Caso o identificador enviado seja diferente do gerado pelo

    rails • 304 – not modified (27) • Sem conteúdo RESPOSTA VAZIA 95 < HTTP/1.1 304 Not Modified 99 < ETag: "d83b123020456902d309159915cf2cad"
  20. • 2 SELECTS e View na primeira requisição • 1

    SELECT na segunda • Sem view na segunda • 46x mais rápido! O QUE ISSO SIGNIFICA? 79 Started GET "/charts" for 127.0.0.1 at 2016-03-10 11:36:28 -0300 80 Processing by ChartsController#index as */* 81 (0.2ms) SELECT COUNT(*) FROM "data_points" 83 DataPoint Load (17.5ms) SELECT "data_points".* FROM "data_points" 84 Rendered charts/index.json.jbuilder (289.8ms) 85 Completed 200 OK in 303ms (Views: 277.9ms | ActiveRecord: 17.7ms) 104 Started GET "/charts" for 127.0.0.1 at 2016-03-10 11:36:30 -0300 105 Processing by ChartsController#index as */* 106 (0.2ms) SELECT COUNT(*) FROM "data_points" 108 Completed 304 Not Modified in 6ms (ActiveRecord: 0.2ms)
  21. There are only two hard things in Computer Science: cache

    invalidation and naming things. Phil Karlton
  22. CACHE DE APLICAÇÃO •Desvantagens • Mais lento • Mais complexo

    de manter •Vantagens • Maior controle • Pode fornecer dados privados
  23. TERMOS UTILIZADOS •Hit • Quando o dado requisitado está presente

    •Miss • Quando o dado requisitado não está presente • Em um miss, a aplicação pega o dado da fonte mais lenta e guarda no cache.
  24. FALANDO EM EXPIRAR… •Simplesmente invalidar o cache com o tempo

    •Utilizar o cache_key do ActiveRecord •Invalidar o cache manualmente em certos pontos da aplicação Temos 3 Opções:
  25. EXPIRANDO COM O TEMPO •Vantagens • Simples • Plug&play -

    Ótima ferramenta para apagar incêndios •Desvantagens • Pode não ser adequado • Causa picos de lentidão quando o cache expira • Usuário vai “sentir" o cache miss
  26. WARMUP TO SAVE THE DAY Se Antes Do Cache, Existisse

    Um Job Que Atualizasse O Mesmo Com Os Dados Novos ?
  27. UTILIZANDO O CACHE_KEY Nada Mais Nada Menos Que O Nome

    Do Modelo Com O Timestamp Do Updated_At
  28. UTILIZANDO O CACHE_KEY •Vantagens • “Não precisa” se preocupar em

    expirar o cache • Se adequa a situações que o cache por tempo não funciona •Desvantagens • Preso a um modelo • Consome mais memória do cache*
  29. • Expirar com o tempo é uma medida para diminuir

    o consumo de memória EXEMPLO TRUSTVOX
  30. AS VEZES NÃO TEMOS ESCOLHA •Vantagens • Controle TOTAL sobre

    quando o cache expira • Engloba os casos que o cache de tempo e o cache_key não cobre •Desvantagens • É preciso invalidar o cache para toda mudança de dado que influencie o mesmo • Na maioria das arquiteturas se torna uma tarefa bastante complexa • > Débito técnico :-(
  31. PRÓXIMOS PASSOS… •É uma péssima idéia ficar adicionando lógica de

    expirar cache nos form objects • Especialmente quando o cache é expirado por efeitos colaterais •Como melhorar isso ? Desacoplando Os Expires Do Form
  32. NOVA ARQUITETURA •Mensagens são postadas e entidades consumem essas mensagens

    • Diminui dependencias • Escalável •Como tudo em programação, pode virar um clusterfuck Mais Orientada A Eventos E Mensagens