DEV Community

Cover image for WordPress, Object Cache e Redis
Felipe Elia
Felipe Elia

Posted on • Originally published at felipeelia.com.br

WordPress, Object Cache e Redis

Originalmente publicado no meu blog.

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!

Índice

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.

Diagrama com duas linhas: na primeira a requisição sai do client, passa pelo servidor de cache e chega até o servidor de origem. Na segunda, o servidor de cache já responde a requisição.
Peguei a imagem lá no blog do KeyCDN.

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.

Hierarquia de memória estendida. Da base para o topo: armazenamento offline (mais lento), disco rígido, memória RAM, cache do processador e CPU (mais rápido). Quando mais rápido, mais caro.
A qualidade não é das melhores, mas a imagem é ótima para entender a relação entre armazenamento e velocidade.

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.

Como habilitar o Redis na Cloudways: Server Management -> Settings & Packages -> Packages -> Redis (Install)

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.

Captura de tela do cabeçalho do plugin WordPress "Redis Object Cache" no repositório de plugins.

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.

Doutor Brown do filme "De volta para o futuro" segurando cabos elétricos e falando "Ready!"

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á!

Top comments (0)