Reindexado en Magento 2: qué, cómo, debuggearlo, consejos, ejemplos…

TLDR

  • No reindexes manualmente ya que tiene un gran impacto en el rendimiento del front
  • “update on save” hace un reindexado de muchas cosas, no solo el producto que has guardado y además limpia FPC (cada vez que guardas un producto)
  • Siempre “update on schedule” en una web en producción, solo actualizará lo relacionado al dato cambiado
  • Se puede debuggear paso a paso el reindexado para intentar encontrar el error y buscar una solución más definitiva que tirar un reindex manual

¿Qué es y como funcionan los índices en Magento?

Índice vs Caché

Los índices (de magento) son básicamente una caché más. La única diferencia es un índice no almacena el dato “crudo”, lo transforma antes.

¿Qué componentes forman parte de un índice?

Dictionary: tabla en base de datos con los datos crudos originales.

Index: tabla en base de datos con la representación de los datos crudos ya transformados (agregados, optimizados para su búsqueda, etc…).

Indexer: aquello que va a transformar los datos crudos del diccionario en datos representados en el índice.

Modos de reindexado “Update on save”:

Se ejecutan los indexadores inmediatamente después de que las entidades se guarden (ej: cuando se guarda un producto en el pabel admin)

  • cuando una entidad se guarda, se “refresca” todo el índice y por tanto, todos sus índices relacionados
  • esto ocurre para todos los productos de la web, no solo el actual
  • además, al “refrescar” un índice de un producto, se limpia la caché (FPC = full page cache)

Cada vez que guardamos un producto en el admin con Update on save, reindexamos todo y borramos cachés. ¿Podemos comprobarlo? Claro.

  • Visitamos una tienda con el contenido demo instalado, plantilla Luma, caches activas con Redis
  • Vamos a la categoría Gear > Bags y visitamos la ficha de producto los 4 primeros
  • Revisamos cache en Redis con
    • redis-cli INFO keyspace
# Keyspace
db0:keys=216,expires=35,avg_ttl=1046474694
db1:keys=33,expires=4,avg_ttl=24469391
db2:keys=1,expires=1,avg_ttl=3429550
  • Nos fijamos en db1 que en mi caso es la FPC: 33 keys y 4 con expires.
    • el número de páginas en Redis, es igual al número de entidades con expires. La cantidad de productos que hemos visitado, los 4 de antes
    • si visitamos uno de esos productos de nuevo, abrimos las devtools de chrome en pestaña network, dejamos solo el “doc” (usando los filtros), podemos ver que tenemos en “headers” x-magento-cache-debug: HIT y ha tardado 115ms–> esto quiere decir que se ha cargado desde la cache, la cache por tanto existía
  • Vayamos al admin y guardamos un producto que no esté relacionado ni en la misma categoría
    • en los headers tenemos x-magento-cache-debug: MISS, es decir no había cache, y ha tardado 466ms…
    • y si miramos Redis, veremos que en el db1 (mi caso esa es la FPC), tiene expires a 0 (o a 1 si hemos visitado ya la página de producto antes ver Redis)
# Keyspace
db0:keys=758,expires=106,avg_ttl=1159049794
db1:keys=32,expires=0,avg_ttl=0
db2:keys=3,expires=3,avg_ttl=2715255

Modo de reindexado “Update on Schedule”

Los indexadores se ejecutan via cron, por defecto cada minuto, no inmediatamente al guardar un producto.

  • Indexa solo la información que se ha guardado (update on save indexaba todo el índice y los índices relacionados).
  • Utiliza un log para registrar los cambios y poder actualizar del índice solo esos registros.
  • Solo las páginas relacionadas a esos registros son quitadas de la cache (FPC)

Podemos realizar la misma prueba que antes con Redis, y veremos que al guardar un producto en el admin y ejecutar los crons, no obtendremos un “expires=0” en la db de FPC.

¿Qué ocurre si esperamos y algo no se actualiza en el front?

Posibles problemas:

  • No hemos esperado lo suficiente: normalmente 1-2 minutos
  • El Crontab no está configurado o los procesos se han atascado
  • Varnish está mal configurado y la página en cuestión no fue limpiada de la caché
  • Problema con reindexado en sí mismo

Posibles soluciones

  • Reindexado manual desde comandos: funcionaría, pero sabemos que también limpiaría la caché con el impacto en el rendimiento que eso significaría
  • Buscar solucionar el problema que ha hecho que no se actualice el front: veremos cómo revisar el último posible problema “problema con el reindexado en sí mismo”

Para ello veremos cómo funciona.

¿Cómo funciona internamente “update on save”?

Elementos implicados: Tareas por Cron, Triggers MySQL, Tablas “changelog”, PHP

Triggers

En el momento de cambiar a “update on save”, se crean en la base de datos los triggers necesarios (y se quitan al cambiar de modo de reindexado).

El proceso de loggear los cambios (registros que han cambiado) para reindexarlos, es completamente independiente de magento. Son los triggers mysql los encargados.

Tablas “changelog”

Cada tabla “dictionary” tiene una tabla “changelog” correspondiente.

Estas tablas se crean sólo la primera vez que hemos cambiado a “update on save” y su nombre depende del nombre del índice, no del nombre de la tabla “dictionary”.

¿Cuál es el proceso hasta aquí?

Estos triggers, detectan un cambio en un registro de la base de datos de alguna entidad, y loggean el id de esa entidad en una tabla “changelog”. Entonces, cuando la tarea por cron activa el reindexado, se procesan estos ids de las tablas “changelog” y se actualiza solo ese dato.

Veamos un ejemplo concreto completo del proceso: stock

Viendo un ejemplo concreto, nos ayudará a saber debuggear un cambio que no se ve en el front, y evitar así tener que tirar un reindexado entero por consola, y por supuesto a encontrar el problema que ocasiona que este cambio no se haya actualizado en el front.

Nos basaremos en este ejemplo en el stock (no MSI) que es el más sencillo.

Tablas implicadas

  • cataloginventory_stock_item
    • la tabla “dictionary”. Tiene la información en crudo
    • al cambiar el stock de un producto en el admin, es aquí donde se almacena el dato
    • product_id será la columna loggeada en la tabla “changelog”
    • si pasamos el reindexado a modo “update on save” veremos que se crea el trigger que nos interesa “trg_cataloginventory_stock_item_after_insert” (entre todos los demás)
  • cataloginventory_stock_cl
    • la tabla “changelog” (por ell el sufijo “_cl”) donde se loggean cambios de la tabla anterior
    • version_id es un autoincremental, es el id de la tabla
    • entity_id es el id del producto cambiado, es decir el product_id de la tabla anterior
  • cataloginventory_stock_status
    • la tabla “index” en sí misma, aquí se cachea el dato de stock ya procesado
    • stock_status es el stock procesado
  • mview_state
    • tiene la información de todos los estados de los procesos de reindexado (mview es “materialized view”)
    • “mode” nos dice el tipo de reindexado (enabled indica update on schedule)
    • “version_id” es el último “version_id” procesado en el indexador, que viene de la tabla “changelog” anterior
    • “view_id” + “_cl” nos da el nombre de la tabla “changelog” ya que view_id contiene el nombre del indexador

Y con todo esto, veamos cómo debuggearlo

Guardar un producto desde el admin, con crons activados

  • Miremos en el admin, el producto con id=1 (Joust Duffle Bag)
    • tiene por defecto quantity=100, y cambiemos el quantity a otro número (por ejemplo 50) y guardamos el producto.
  • Miremos ahora cataloginventory_stock_item (la tabla “dictionary”) y miremos el producto con id=1 (Joust Duffle Bag)
    • se ha cambiado aquí el qty por el nuevo que hemos introducido, ahora es 50
  • Miremos cataloginventory_stock_cl (la tabla “changelog”) y mremos el producto con entity_id=1
    • el último version_id es 32
  • Miremos mview_state (estado de los procesos de reindexado)
    • cataloginventory_stock también es 32, estamos al día, todo ha sido actualizado
  • Miremos cataloginventory_stock_status (tabla “index”)
    • qty es lo que pusimos, ha pasado de 100 a 50, debería verse en el front esa cantidad

Ahora, cambiemos el qty desde la base de datos del mismo producto:

  • Miremos la tabla cataloginventory_stock_item y cambiemos el stock a otra cosa (por ejemplo 60)
  • Vemos que en cataloginventory_stock_cl también está el cambio registrado
    • version_id es 1 más que antes, ahora es 33
  • Podemos revisar mview_state
    • el version id no es 33, no estamos al día. Es 32, uno menos de lo que debería para estarlo
  • Ejecutemos crons manualmente
  • Podemos revisar mview_state de nuevo
    • deberíamos ver el mismo número en cataloginventory_stock que el version_id de la tabla changelog, porque estamos con todos los datos actualizados

¿Qué podemos revisar si vemos que no se actualiza el quantity en el front?

  • Podemos revisar si el version_id de cataloginventory_stock_cl, coincide o no con el version_id de mview_state
    • si no coincide es que no están sincronizadas, por tanto cuando pase el cron no ejecutará un reindexado realmente por lo que el dato nunca será trasladado a la tabla index en cuestión que es la que se mostrará en el front
    • si vemos que el version_id de mview_state por algún motivo se ha disparado respecto a la tabla changelod (es por ejemplo 200 en mview_state y 150 en la changelog) esto implicaría que deberíamos hacer esa cantidad de cambios (la diferencia, en este caso 50) para que todo se volviera a procesar
  • Podemos hacer bin/magento indexer:status
    • lo que veamos marcado con “in backlog” son cambios que aún no han sido procesados (se han añadido a las tablas changelog, pero el cron aún no los ha podido leer o no los puede leer por algún motivo)
  • Si tenemos que reindexar finalmente, es mejor hacerlo así
    • bin/magento indexer:set-mode realtime && bin/magento indexer:reset && bin/magento indexer:reindex && bin/magento indexer:set-mode schedule
    • esto es importante para no interferir con el reindexado que ejecute el cron, por ello lo pasamos primero a “update on save”, invalidamos indices, reindexamos completamente y volvemos al modo “update on schedule”

¿Qué ocurre con las URL rewrites?

  • No son indexadas en Magento 2
  • Sólo se generan cuando guardas producto, categoría o página
  • Si queremos regenerar las URL desde comandos https://github.com/elgentos/magento2-regenerate-catalog-urls

NOTA: todo el mérito es para John Hughes en su vídeo https://youtu.be/YKEXjvNCIjk “Stop Refreshing the @#$%&! Indexes”