em aplicações
web
cache
@jcemer
jcemer.com
twitter.com/jcemer
globo.com
talentos.globo.com
Cache is a hardware or
software component that
stores data so future
requests for that data
can be served faster
- Wikipedia
There are only two 

hard things in computer
science: cache invalidation
and naming things.
- Phil Karlton
😓
Caching and RAM is the
answer to everything
- about Flickr Architecture
Cache é um dos
segredos da
escalabilidade e
boa performance
esta palestra é sobre
algumas práticas utilizadas
na globo.com e em
especial no Globo Play
😊
Onde pode haver cache?
• navegador do usuário
• rede mundial de
computadores
• servidor da sua aplicação
navegador
cache no
Caching would be useless
if it did not significantly
improve performance
- HTTP/1.1 specification
Recursos necessários para
exibir uma página podem
ser reutilizados durante a
navegação
• documentos
• imagens
• scripts e folhas de estilo
• requisições assíncronas
O que pode ser cacheado?
por alguns minutos
por alguns meses
por alguns meses
por alguns minutos
Os cabeçalhos de resposta
do HTTP estabelecem
como e se o cache pode
ser feito
GET /main.css HTTP/1.1
Host: jcemer.com
GET /main.css HTTP/1.1
Host: jcemer.com
HTTP/1.1 200
Date: Tue, 13 Sep 2016 13:32:50 GMT
Cache-Control: max-age=604800
guarde por 7 dias!
permite que a resposta da
requisição seja
armazenada no cache
Cache-Control: max-age=604800
Cabeçalho de resposta
folha de estilo é
requisitada uma única vez
durante a navegação
😄 😄
Novas requisições são
disparadas quando o
recurso está expirado ou
o usuário força a
atualização da página
😁
permitem adicionar mais
informações sobre o
recurso
Last-Modified: Mon, 12 Sep 2016 22:06:39 GMT

Etag: W/"337e7-8HrLmYe6UGIUDolQeGLoyw"
Cabeçalhos de resposta
GET /main.css HTTP/1.1
Host: jcemer.com
HTTP/1.1 200 OK
Date: Tue, 13 Sep 2016 13:32:50 GMT
Last-Modified: Mon, 12 Sep 2016 15:23:17 GMT
Cache-Control: max-age=604800
informação nova
permitem reaproveitar o
recurso caso a cópia em
cache ainda seja válida
If-Modified-Since: Mon, 12 Sep 2016 15:23:17 GMT
If-Match: W/"337e7-8HrLmYe6UGIUDolQeGLoyw"
Cabeçalhos de requisição
GET /main.css HTTP/1.1
Host: jcemer.com
If-Modified-Since: Mon, 12 Sep 2016 15:23:17 GMT
GET /main.css HTTP/1.1
Host: jcemer.com
If-Modified-Since: Mon, 12 Sep 2016 15:23:17 GMT
HTTP/1.1 304 Not Modified
Date: Tue, 13 Sep 2016 13:32:50 GMT
Last-Modified: Mon, 12 Sep 2016 15:23:17 GMT
Cache-Control: max-age=604800
<EOF>
Práticas como esta
modificam completamente
a experiência do usuário
rede
cache na
Content delivery network
(CDN) is a globally
distributed network 

of proxy servers
- Wikipedia
permite dar liberdade para
servidores de cache
intermediários
Cache-control: public
Documentação de como fazer
cache dos vídeos da globo.com
https://github.com/globocom/Globo-Live-Cache
Documentação de como fazer
cache dos vídeos da globo.com
TL;DR 

respeite os cabeçalhos
das respostas
https://github.com/globocom/Globo-Live-Cache
servidor
cache no
cache server
O servidor de cache
intermedia a comunicação
com a aplicação ou
demais servidores
• imagens
• scripts e folhas de estilo
• documentos comuns a
todos os usuários
O que pode ser cacheado?
por alguns meses
por alguns meses
por alguns minutos
• Varnish
• Squid
• nginx
Quais ferramentas existem?
https://varnish-cache.org
http://www.squid-cache.org
https://www.nginx.com
nginx pode operar como
um proxy reverso e
como cache
location / {
proxy_pass http://otherserver;
}
todas as requisições são
repassadas para outro
servidor
proxy_cache_path /path/to/cache;


location / {
proxy_pass http://otherserver;
proxy_cache cache;
}
caching ativado!
😄
Os cabeçalhos das
respostas definem os
tempos de cache assim
como em um navegador
t1
t2
t1
requisições em
tempos diferentes
t1
t1
requisições em um
mesmo instante!
😓
proxy_cache_lock on;
proxy_cache_lock_timeout 180;
apenas a primeira request
por determinado recurso
é encaminhada
t1
t1
t1
😄
Requisições subsequentes
a um recurso ficarão
aguardando que o cache
seja atualizado
😞
e caso minha rede
interna falhe?
proxy_cache_use_stale timeout error http_500;
permite entregar
conteúdo antigo caso
algum erro aconteça
😄 😞
e caso minha
aplicação falhe?
🤒
proxy_cache_use_stale http_500;
permite entregar
conteúdo antigo caso a
aplicação retorne erro
😄 🤒
Esta prática pode garantir
que seu produto não caia
Cache é também
uma excelente
estratégia de
tolerância a falhas
proxy_cache_use_stale updating;
permite entregar
conteúdo antigo para
requisições subsequentes
Varnish e Squid possuem
estratégias melhores para
revalidar o cache sem
penalizar o usuário
http://serverfault.com/questions/576402/nginx-serving-stale-cache-
response-while-updating
aplicação
cache na
Cache na aplicação é
utilizado para diminuir o
tempo de resposta de
determinadas operações
• computações complexas
• dados comuns a certas
requisições
O que pode ser cacheado?
def program
@program ||= Program.find(@program_id)
end
Video class
Memoization é a prática
de armazenar o resultado
de operações para evitar
futuras execuções
def program
@program = Program.find(program_id) unless 

defined? @program
end
https://github.com/rails/rails/commit/
36253916b0b788d6ded56669d37c96ed05c92c5c
Video class
Memoization é ideal
para operações de
objetos instanciados
durante uma requisição
Memoization em objetos
instanciados durante uma
requisição possibilita 

pouco reuso
😓
$program = Program.find(program_id)
😓
Objetos instanciados
uma única vez no ciclo
de vida da aplicação
servem como 

in-memory cache
cache.fetch(program_id) do
Program.find(program_id)
end
http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html
ActiveSupport::Cache::Store
Resultados armazenados
em objetos vitalícios
necessitam de um 

tempo de expiração
😓
cache.write(key, value, expires_in: 1.minute)
http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html
ActiveSupport::Cache::Store
node-cache
https://github.com/ptarjan/node-cache
cache.put(key, value, 6000)
Toda aplicação possui um
limite prático de memória 😓
https://github.com/ptarjan/node-cache/issues/77
😓
node-cache: projeto com 540 stars
🔥
when the cache exceeds
the allotted size, a cleanup
will occur which tries to
prune the cache down
ActiveSupport::Cache::
MemoryStore
A moda agora é escalar
horizontalmente as
aplicações em diversos
containers
😓
https://tsuru.io
Never assumes that
anything cached in
memory or on disk
will be available on 

a future request
https://12factor.net/processes
Uma storage cache-valor
pode servir como cache
para seus containers
• Redis
• Memcached
Quais storages chave
+valor existem?
http://redis.io
http://memcached.org
http://redis.io

gem redis-store
Redis suporta diferentes
políticas para gerir o
máximo espaço
consumido
noeviction
allkeys-lru 

evict keys trying to remove the less recently
used keys first
volatile-lru 

equals allkeys-lru but only among keys that
have an expire set
http://redis.io/topics/lru-cache
O uso de um serviço para
cache introduz um ponto
único de falha
😓
redis
ativado
* gráfico do API gateway do Globo Play
Redis suporta tolerância
a falhas através de
persistência em disco,
replicação e sentinel
http://redis.io/topics/persistence

http://redis.io/topics/replication

http://redis.io/topics/sentinel
Sharding pode escalar o
cache horizontalmente e
melhorar a tolerância a
falhas
https://github.com/twitter/twemproxy
http://www.slideshare.net/ShashiShekarMadappa/evcache-at-netflix

https://github.com/Netflix/EVCache
http://www.slideshare.net/ShashiShekarMadappa/evcache-at-netflix

https://github.com/Netflix/EVCache
http://www.slideshare.net/ShashiShekarMadappa/evcache-at-netflix

https://github.com/Netflix/EVCache
https://www.youtube.com/watch?v=Rzdxgx3RC0Q
Diversas instâncias da
aplicação poderão tentar
revalidar o resultado 

ao mesmo tempo
😓
dado expirado 

no cache!
😁
cache.fetch(key, race_condition_ttl: 10.seconds) do

heavy_db_computation

end
http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html
ActiveSupport::Cache::Store
race_condition_ttl
revalida o cache editando
o valor armazenado
race_condition_ttl
revalida o cache editando
o valor armazenado
mas a leitura e escrita
não é transacional!
😁
😳
Solução #1
Utilizar algum artifício de
query cache no próprio
banco de dados
😒
Solução #2
Manter o cache sempre
válido com a ajuda de um
worker
🙂
Solução #3
Reescrever toda sua
aplicação em Elixir
😎
Solução #4
Criar um micro-serviço
para esta operação crítica
protegido por um
servidor de cache
😍
😄
nginx pode ser um ótimo
aliado para proteger
serviços internos
https://github.com/plataformatec/faraday-http-cache
Respeite os
cabeçalhos HTTP
#1
Cache é
assunto sério
#2
Monitore hit/miss
e outras métricas
da sua estratégia
de cache
#3
Pondere bem antes
de escolher uma
estratégia de cache
#4
foi um prazer
obrigado
@jcemer

Cache em aplicações web