Performance

Indexador em Update by Schedule e as tabelas _cl do Magento 2

O changelog do Magento é uma fila numerada. Zerar a numeração de um lado sem zerar do outro é o jeito mais silencioso de deixar preço e estoque desatualizados por semanas.

Por Roger Takemiya · Publicado em · 6 min de leitura

Loja com catálogo grande e ERP mandando preço o dia inteiro: salvar produto no admin demora, o import trava, e alguém liga o Update by Schedule para tirar o reindex do caminho. Até aí, ótimo — é a recomendação da própria Adobe para site grande e com muita atualização.

O problema aparece semanas depois. O disco enche, o DBA vê uma tabela terminada em _cl com trinta milhões de linhas, dá um TRUNCATE e libera 20 GB. No dia seguinte o preço novo do ERP não chega mais na loja, e o indexer:status continua dizendo Ready.

O que o schedule liga de verdade

O comando é este, e vale por indexador:

php bin/magento indexer:set-mode schedule catalog_product_price
php bin/magento indexer:show-mode

Por baixo, o Magento faz duas coisas. Cria triggers no MySQL nas tabelas que aquele indexador observa, e cria uma tabela de changelog chamada <view_id>_cl — daí catalog_product_price_cl, cataloginventory_stock_cl, catalogsearch_fulltext_cl. Ela tem só duas colunas: version_id, um bigint auto_increment, e entity_id.

Quem controla o que já foi processado é a tabela mview_state, com as colunas view_id, mode, status, updated e version_id. Dá para ver tudo de uma vez:

SELECT view_id, mode, status, version_id, updated FROM mview_state ORDER BY view_id;

O trabalho fica com o cron do grupo index, declarado no crontab.xml do Magento_Indexer: indexer_update_all_views roda a cada minuto e processa do version_id guardado no state até o maior version_id que existe no _cl. Sem cron rodando, schedule não indexa nada — e não existe aviso na tela.

Por que a _cl cresce tanto

Cada linha da tabela observada que muda vira uma linha no _cl. Import de 80 mil produtos com preço e estoque? São centenas de milhares de linhas em minutos. A limpeza existe: o job indexer_clean_all_changelogs, do mesmo grupo index, roda a cada 5 minutos e apaga o que já foi processado.

Se a tabela está monstruosa, é porque a limpeza não está acontecendo. Motivos, em ordem de frequência:

  • cron parado ou o grupo index desabilitado;
  • status travado em working no mview_state: o código do mview começa com if (!$this->isIdle() || !$this->isEnabled()) return;, então uma execução que morreu no meio deixa a view parada para sempre;
  • indexador em suspended, que alguém deixou assim depois de uma migração.

Veja o tamanho antes de decidir qualquer coisa:

SELECT table_name, table_rows, ROUND(data_length/1024/1024) AS mb
FROM information_schema.tables
WHERE table_schema = DATABASE() AND table_name LIKE '%\_cl'
ORDER BY data_length DESC;

O TRUNCATE que congela o indexador

Aqui está a armadilha. TRUNCATE no MySQL zera o AUTO_INCREMENT: a próxima linha do catalog_product_price_cl volta a ser version_id = 1. Só que o mview_state daquela view continua guardando, digamos, 28471903.

O método update() do Magento\Framework\Mview\View compara os dois e sai calado:

$lastVersionId = (int)$this->getState()->getVersionId();
if ($lastVersionId >= $currentVersionId) {
    return;
}

Como 28.471.903 é maior que 1, o cron passa de minuto em minuto e não faz nada. Nenhum erro no log, nenhum indexador marcado como inválido, o indexer:status segue verde. Você só descobre quando o cliente reclama que o preço da promoção não entrou.

O jeito certo de zerar o changelog não é SQL, é CLI:

php bin/magento indexer:set-mode realtime catalog_product_price
php bin/magento indexer:set-mode schedule catalog_product_price

Voltar para realtime chama o unsubscribe(), que remove as triggers, dropa a tabela _cl e — a parte que importa — grava version_id = 0 no state. Ainda marca o indexador como inválido, então o cron reindexa sozinho. Ligar schedule de novo recria tabela e triggers do zero, com os dois lados na mesma página.

Se você já deu o TRUNCATE e não quer refazer a volta toda, o mínimo é acertar o state na mão, com backup antes:

UPDATE mview_state SET version_id = 0 WHERE view_id = 'catalog_product_price';

Antes de ligar o schedule na sua loja

Quatro coisas que eu confiro sempre, nessa ordem:

  • Cron funcionando. Sem o grupo index rodando, schedule é pior que Update on Save: nada indexa e ninguém percebe.
  • Espaço em disco e retenção. Se a _cl já está grande, resolva a causa (cron, status travado) antes de mexer na tabela.
  • As triggers depois de restaurar um dump. O mysqldump traz trigger por padrão, mas quem exporta com --skip-triggers, usa ferramenta gráfica ou restaura com um usuário sem privilégio de TRIGGER acaba com o banco sem elas. Confira com SHOW TRIGGERS; antes de culpar o cron.
  • Customer Grid. Até o 2.4.7, esse indexador só funcionava em Update on Save. Do 2.4.8 em diante ele aceita os dois modos e já vem em Update by Schedule.

E uma manha de operação: depois de qualquer import gigante, olhe o mview_state. Se alguma view estiver em working há mais de alguns minutos, ela travou. Marcar como idle e deixar o cron seguir costuma resolver — e é bem mais barato que descobrir isso três semanas depois.

Perguntas rápidas

Como sei se o meu indexador em schedule está realmente rodando?

Compare duas coisas: o maior version_id da tabela _cl e o version_id daquela view no mview_state. Se o do state está muito atrás e não sobe de um minuto para o outro, o mview parou. Se os dois andam juntos, está saudável.

Posso deixar todos os indexadores em Update by Schedule?

Em loja com catálogo grande e atualização frequente, é o que a Adobe recomenda. A única exigência é ter cron confiável rodando. Em loja pequena, com pouca alteração, Update on Save é mais simples de operar e não faz diferença de performance.

Perdi as triggers depois de restaurar um dump. Como recrio?

Rode indexer:set-mode realtime e depois indexer:set-mode schedule nos indexadores afetados. O segundo comando recria as triggers e a tabela de changelog. Confira depois com SHOW TRIGGERS no banco.

Pra conferir na fonte

  1. Manage indexers — Adobe Experience League
  2. Indexer configuration best practices — Adobe Experience League
  3. Magento\Framework\Mview\View — GitHub
  4. Magento_Indexer crontab.xml — GitHub

indexador mysql cron

Precisa de um orçamento? Ficarei feliz em ajudar. Clique Aqui