Cache! A-ah. Salvador do Universo

Cache

O cache é incrível. Em vez de repetir uma operação cara, apenas reutilize o
resultado da última vez! Mas usar o cache corretamente pode ser complicado … Mas há
algumas coisas que eu gostaria de não ter que lidar:

Proteção contra estouro

O cache é essencial se você tiver um aplicativo de alto tráfego. Mas, a menos que haja um
fluxo constante de tráfego previsível, você corre o risco de não ter dados no cache no
momento em que mais precisa: quando o tráfego aumenta.

Quando, de repente, há muitas solicitações simultâneas de dados que
não estão no cache, a operação cara que gera esses dados será executada
muitas vezes, todas de uma vez. Com tráfego suficiente, o servidor de aplicativos provavelmente
não será capaz de lidar com isso e travar.

Deve ser possível para a primeira solicitação sinalizar aos outros que
está computando o código caro. Deve dizer aos outros para não fazerem isso
também, mas apenas aguardar pelo resultado. Nem todo processo deve
ter que calcular o mesmo resultado que o primeiro já está trabalhando …

Pedidos repetidos

Dado um aplicativo suficientemente grande, é provável que você busque o
mesmo valor várias vezes, porque simplesmente precisa dele em vários lugares
em seu aplicativo. Como seu armazenamento de valor-chave provavelmente está em outro servidor,
você provavelmente está perdendo um valioso milissegundo para cada solicitação repetida.

Depois que um resultado é obtido do cache, ele pode ser mantido na memória. Quando
mais tarde você tentar buscar a mesma chave na mesma solicitação, ela poderá ser
servida apenas da memória. Ou quando você armazena um novo valor e precisa dele novamente mais tarde, isso
também pode ser duplicado na memória. Apenas certifique-se de remover os dados da memória
antes que eles se encham completamente e façam com que seu aplicativo trave.

Transações

Muitos dos dados armazenados no cache são desnormalizações de dados que também estão no
armazenamento de dados primário (por exemplo, banco de dados) ou podem ser derivados dele. Isso pode afetar as
atualizações em várias tabelas ou acontecer em todo o código. Para garantir a
integridade dos dados no banco de dados, posso usar transações, mas onde isso deixa meu
cache?

Semelhante às solicitações de cache de repetição em buffer (na memória), devemos ser capazes de
bufferizar ” as gravações e armazená-las todas de uma vez, quando estivermos prontos para isso. Em vez
de armazenar imediatamente os dados em cache, pode ser adiado até que eu queira confirmá-
los. Enquanto isso, também podemos gravá-lo na memória para que o valor
possa ser reutilizado , mesmo que ainda não tenha sido persistido no cache real. Isso permitiria até
algumas otimizações, como não armazenar dados em uma chave que vamos excluir
novamente na mesma solicitação. Ou combinando várias setoperações separadas em
uma setMulti.

Mas a atomicidade é a parte complicada aqui. Se uma das operações atrasadas falhar,
o resto não deve passar e o que já aconteceu deve ser restaurado
ao que era. Isso pode ser feito ordenando as operações de uma
maneira cuidadosa: primeiro execute as operações “condicionais” (por exemplo, replace
só pode acontecer se um valor já existir no cache), depois faça aquelas em que nenhuma
falha é esperada, independentemente do que possa ter acontecido a cache antes de
confirmarmos (por exemplo delete, sempre será bem-sucedido).

E para garantir que possamos realmente recuperar se uma das operações condicionais falhar,
devemos primeiro recuperar seu valor atual. Podemos colocá los cas
( verificar e definir ) de volta
se eles derem errado! O único problema aqui pode ser restaurar o tempo de expiração,
que a maioria dos armazenamentos de valor-chave não permite que você busque.

Compatibilidade

Idealmente, haveria apenas 1 API para uma variedade de back-ends de cache. Os
projetos de código aberto devem oferecer suporte a várias plataformas. Ou talvez você esteja executando um
cluster de servidores Redis em produções, mas gostaria de usar um
cache baseado em memória para executar seus testes?

O PHP FIG decidiu recentemente no php / cache 1.0 ,
uma interface a ser implementada se você gostaria de poder usar uma ampla variedade de
back-ends de cache. Não oferece muito além do get& básico set, mas
servirá na maioria dos casos!

Tenho certeza de que muitas implementações de psr / cache surgirão, mas já existem algumas
. Há um em que estou trabalhando: o álbum de recortes .
Ele vem com adaptadores para Memcached ,
Redis ,
Couchbase ,
APC ,
MySQL ,
PostgreSQL ,
SQLite ,
sistema de arquivos (via league / flysystem )
e um cache na memória (muito
útil para testes). E tem todas as vantagens mencionadas acima:
proteção contra estouro ,
cache em buffer e
transações (aninhadas) .
Tudo isso por trás de uma API simples do tipo Memcached
(para recursos máximos) ou psr / cache
(para compatibilidade máxima).