Quer um jeito fácil de deixar o seu site WordPress mais rápido e ainda por cima dar um up no seu nível de programação com WP? O Object Cache pode ser a resposta para as duas coisas!
No post sobre o que é uma API, comentei brevemente sobre o cache de objetos do WordPress e como eu uso o Redis aqui no blog. Nesse post vamos dar uma olhada melhor nessas duas coisas.
Antes de Object Cache… O que é cache?
Antes de falarmos sobre Redis, precisamos falar sobre Object Cache. E antes de falarmos sobre Object Cache, você adivinhou, vamos falar rapidamente sobre cache em geral.
Procurando por definições sobre o que é cache na internet, o senso comum parece ser algo como o seguinte:
Cache é um lugar de armazenamento intermediário e acesso rápido, localizado entre o consumidor e o armazenamento principal, potencialmente economizando uma viagem mais demorada.
Veja o exemplo abaixo. Na primeira vez a requisição vai até o servidor final, mas o servidor de cache guarda uma cópia. Na segunda vez é servida a cópia, economizando a viagem até o servidor.
Com isso em mente, podemos pensar nas várias viagens necessárias para abrir um site e nos vários intermediários que podemos inserir para economizar viagens:
Browser cache | Entre o seu navegador e o servidor do site, se houver uma cópia válida do site no seu computador, economizamos uma viagem. Na verdade, a viagem inteira nesse caso. |
CDN | Se seu site passar por um CDN como Cloudflare e se o conteúdo gravado no CDN ainda for válido, não há porque solicitar uma nova cópia, basta enviar a antiga e economizamos uma viagem. |
Cache do WP | No seu site WordPress, se um post já foi trazido do banco de dados uma vez, se a informação que temos na memória ainda é valida, por que ir até o banco de dados novamente? |
Existem muitas outras viagens em que podemos adicionar um lugar intermediário para salvar uma versão da informação esses são só alguns exemplos. Ah, e a pronúncia é a mesma que CASH (dinheiro), não cachê ou qualquer outra coisa.
O Object Cache do WordPress
O Object Cache do WordPress é simples e entender como ele funciona pode ser um diferencial na sua próxima entrevista de emprego 😉
Como falei no post sobre o que é uma API, acesso a memória é sempre mais rápido que acesso a disco. Com isso em mente, se no início do processamento de uma página já trouxemos um post para memória, não há necessidade de irmos no banco novamente para buscá-lo.
O problema é que, na implementação padrão do WordPress, ele só ficará na memória até o fim do processamento daquela página. Precisa dele novamente se o usuário recarregar a página? Vamos ter que ir no banco de dados novamente.
O MySQL é inteligente o suficiente para notar se uma informação já foi solicitada recentemente e deixá-la disponível mais rapidamente, mas mesmo assim não é tão rápido. E, no fim das contas, se estamos indo até o MySQL, estamos fazendo uma viagem maior de qualquer forma.
Por padrão, esse post solicitado no começo do processo (ou qualquer outra informação que quisermos) é gravado usando uma classe chamada WP_Object_Cache. Seu objetivo principal é economizar viagens ao banco de dados e ela funciona basicamente como um par chave-valor. O problema, como eu disse, é que isso tudo só dura uma requisição. Para a próxima, fazemos tudo de novo.
Como “gravar” coisas na memória? Redis ou Memcached
Programas como Redis ou Memcached são bancos de dados que armazenam informações em memória. Eles podem fazer mais do que isso, principalmente o Redis, mas para o nosso problema com o WordPress, o armazenamento em memória de pares chave-valor do Redis é tudo o que realmente precisamos.
Ao longo do post vou usar o Redis como exemplo, mas a instalação e utilização com memcached é bem parecida.
O que eu preciso para usar Object Cache com Redis no WordPress?
A lista de pré-requisitos para usar o Object Cache do WordPress com Redis é bem simples:
- Um site WordPress
- Redis funcionando na sua infraestrutura
- Um plugin WordPress para conectar o WP ao Redis
Para o segundo item, o mais rápido é entrar em contato com sua empresa de hospedagem. Na Cloudways, por exemplo, você precisa apertar um botão no Painel para instalar o Redis.
O plugin Redis Object Cache
Como eu disse no outro post, o plugin que eu uso é o Redis Object Cache. Instalá-lo e configurá-lo é relativamente simples. Por padrão, ele tentará se conectar a uma instância do Redis localizada no próprio servidor (127.0.0.1
) na porta 6379
usando o banco de dados Redis de número 0
.
Para que o plugin funcione, ele precisa copiar o arquivo object-cache.php
de dentro de sua própria pasta includes para a pasta wp-content da sua instalação WordPress. Se por qualquer motivo, como permissão de diretórios, não for possível copiar o arquivo automaticamente, será necessário copiá-lo manualmente.
Mesmo Redis com diferentes sites no mesmo servidor
O meu outro blog, o Melancia na Cabeça, também é armazenado neste servidor e compartilha a mesma instalação do Redis. Para evitar que os valores armazenados pelos dois sites entrem em conflito, eu precisei definir define( 'WP_REDIS_DATABASE', 0 );
em um e define( 'WP_REDIS_DATABASE', 1 );
no outro.
Os parâmetros de configuração diferentes do padrão precisam ser adicionados no arquivo wp-config.php do seu site. A lista completa de parâmetros está na wiki do plugin.
Como o plugin de Object Cache funciona
Na função wp_start_object_cache() (trechos abaixo), executada no início do fluxo de carregamento do WordPress, ele detecta a existência ou não do drop-in object-cache.php
. Se disponível, o arquivo é carregado, caso contrário a implementação padrão entra em ação. Então, o cache é instanciado.
function wp_start_object_cache() {
...
if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
require_once WP_CONTENT_DIR . '/object-cache.php';
...
}
...
// Se não houver object cache externo, usa o padrão do WP.
if ( ! wp_using_ext_object_cache() ) {
require_once ABSPATH . WPINC . '/cache.php';
}
require_once ABSPATH . WPINC . '/cache-compat.php';
...
wp_cache_init();
...
}
O conteúdo do drop-in object-cache.php
do Redis Object Cache pode ser visto aqui. Ele é formado por uma implementação da classe WP_Object_Cache
(mais pro final do arquivo) e das várias funções que a utilizam como wp_cache_add
, wp_cache_get
e wp_cache_delete
.
Dando uma olhada em código
Se você precisa de código para entender as coisas melhor, eu entendo. Também sou assim. Aqui está uma parte do caminho executado quando você chama get_post( 123 );
no WordPress 6.0.1.
O código começa em wp-includes/post.php, onde get_post chama WP_Post::get_instance( $post )
.
E então em wp-includes/class-wp-post.php:
final class WP_Post {
...
public static function get_instance( $post_id ) {
$_post = wp_cache_get( $post_id, 'posts' );
if ( ! $_post ) {
...
wp_cache_add( $_post->ID, $_post, 'posts' );
}
...
}
}
Primeiro se verifica se o cache existe (wp_cache_get
). Se existir é usado, senão o post é buscado e salvo no cache (wp_cache_add
).
E só para dar uma olhada no conteúdo da função wp_cache_add
, veja como ela nada mais é que um envelope para o método add
da instância da classe WP_Object_Cache
armazenada na variável global $wp_object_cache
.
function wp_cache_add( $key, $data, $group = '', $expire = 0 ) {
global $wp_object_cache;
return $wp_object_cache->add( $key, $data, $group, (int) $expire );
}
As funções de cache disponíveis no WordPress
A documentação oficial lista algumas das funções de cache mais importantes e estou transcrevendo-as aqui. O próprio core faz uso delas em vários lugares, mas você também pode chamá-las em seu tema ou plugin.
wp_cache_add( $key, $data, $group = '', $expire = 0 ) // Se existe, não sobrescreve
wp_cache_set( $key, $data, $group = '', $expire = 0 ) // Se existe, sobrescreve
wp_cache_replace( $key, $data, $group, $expire ) // Se não existe, não faz nada
wp_cache_get( $key, $group = '', $force = false, $found = null )
wp_cache_delete( $key, $group = '' )
wp_cache_flush()
Chave, valor, grupo e expiração
A maioria das funções wp_cache_*
usam quatro parâmetros:
- Chave: o identificador do objeto armazenado em cache. O ID do post, por exemplo
- Valor: o objeto armazenado propriamente dito. O próprio post.
- Grupo (opcional): o grupo serve para — você adivinhou — agrupar diferentes chaves. Antes servia mais para organização, mas no WordPress 6.1, será possível chamar a nova função
wp_cache_flush_group
para apagar todos os objetos de um determinado grupo, como por exemplo, tudo armazenado em cache de um determinado plugin. - Expiração (opcional): por quanto tempo o cache é considerado válido. Vamos dar uma olhada mais a fundo nisso.
Expiração
Quando falamos de cache, um conceito importantíssimo é tempo de expiração. A informação que temos ainda é válida? Essa é uma parte chave de qualquer implementação de cache, incluindo o Object Cache do WordPress.
Então, como decidimos por quanto tempo um objeto em cache deve ser considerado válido?
Baseado em um evento
Vamos supor que você está armazenado em cache a lista de posts com maior conteúdo. Você pega todos os posts, passa o filtro the_content
em todos eles e mede quantas letras cada um tem. Bem intenso, certo? Isso merece ser armazenado em cache.
O que pode fazer o resultado deste processo mudar? Só um post novo ser criado ou algum existente ser editado, certo?
Neste caso, nosso cache não precisa expirar. Basta invalidar o resultado que temos usando um hook relacionado, nesse caso, a criação de um novo post ou edição de um post que já existe.
Baseado em tempo
Se o que você está armazenando vem, por exemplo, de um ambiente externo como uma API, o evento que invalida o cache não está sob nosso controle. Neste caso, precisamos verificar o resultado de tempos em tempos na API externa e atualizar o valor que temos armazenado.
Transients API e Object Cache
Se você leu o post ou assistiu ao vídeo sobre a Transients API do WordPress, a ideia principal aqui não é novidade: um par de chave-valor que evita um processo demorado é exatamente a ideia por trás das duas coisas. De fato, em um certo aspecto elas são a mesma coisa. Veja a implementação da função get_transient
:
function get_transient( $transient ) {
if ( wp_using_ext_object_cache() || wp_installing() ) {
$value = wp_cache_get( $transient, 'transient' );
} else {
...
}
...
}
Basicamente, se você está usando um mecanismo externo para Object Cache, o transiente será gravado lá. Caso contrário, o banco de dados normal será usado. Interessante, não é? Dá uma olhada lá!
A reserva de cache do Cloudflare (NOVO) seria algo parecido?
Na verdade não, Filipe. Qualquer cache servido pela Cloudflare vai estar num nível mais “alto”: não vai se comunicar com o seu WordPress, mas entender seu site como um conjunto de HTML/CSS/JS, que é o que o seu navegador entende no fim das contas.
Oi Felipe, parabéns pelo post, incrivelmente detalhado para explicar como a brincadeira começa com Redis no wordpress. Aproveitando este seu conhecimento tão avançado com Redis no WP, você já tentou remover o grupo “plugins” da lista de grupos negados que o Redis não vai salvar? Eu sempre tive essa curiosidade, mas tenho medo de testar no site em produção e com preguiça de lançar um stage agora rsrs
Em tese, não deveria dar nenhum problema né? Eu gostaria de saber o que observar depois de permitir que o Redis interaja com os plugins para verificar possíveis conflitos.
Praticamente todos os plugins de object cache com Redis já vem com o parâmetro “WP_REDIS_IGNORED_GROUPS (default: [‘counts’, ‘plugins’])”
Fala, Moisés. Obrigado pelo comentário! Eu nunca tentei, mas o único conflito que eu imagino é se você quiser desabilitar o próprio plugin do Redis. Se testar e puder voltar aqui pra dizer o que aconteceu eu agradeço, você me deixou curioso. Abraços!
Grande Felipe! Parabéns pelo artigo. Muito completo e bem explicativo 🙂