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-modePor 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
indexdesabilitado; - status travado em
workingnomview_state: o código do mview começa comif (!$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_priceVoltar 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
indexrodando, schedule é pior que Update on Save: nada indexa e ninguém percebe. - Espaço em disco e retenção. Se a
_cljá está grande, resolva a causa (cron, status travado) antes de mexer na tabela. - As triggers depois de restaurar um dump. O
mysqldumptraz trigger por padrão, mas quem exporta com--skip-triggers, usa ferramenta gráfica ou restaura com um usuário sem privilégio deTRIGGERacaba com o banco sem elas. Confira comSHOW 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
- Manage indexers — Adobe Experience League
- Indexer configuration best practices — Adobe Experience League
- Magento\Framework\Mview\View — GitHub
- Magento_Indexer crontab.xml — GitHub