Come funziona DAVVERO l’indicizzazione in Magento 2 / Adobe Commerce

La maggior parte degli sviluppatori Magento 2 conosce questo comando:
bin/magento indexer:reindex
Ma cosa succede realmente tra la modifica di un prodotto nel pannello Admin e la visualizzazione del prezzo aggiornato, dell’associazione a una categoria o dei nuovi dati di ricerca sul frontend?
L’indicizzazione di Magento 2 / Adobe Commerce è molto più di un semplice comando che ricostruisce alcune tabelle del database.
Dietro alla modalità Update by Schedule esiste un’architettura basata su:
- rilevamento delle modifiche;
- trigger MySQL;
- tabelle di changelog;
- Materialized View (MView);
- elaborazione incrementale;
- stato degli indexer;
- elaborazione a batch;
- dimensioni e scope;
- e, per gli indexer che lo supportano, tabelle replica e table switching.
Comprendere questa architettura è estremamente utile quando bisogna diagnosticare problemi come:
- prodotti che mostrano prezzi non aggiornati;
- categorie che non si aggiornano;
- risultati di ricerca obsoleti;
- indexer apparentemente bloccati in stato
Processing; - tabelle
*_clche continuano a crescere; - processi cron che consumano CPU in modo anomalo;
- full reindex che richiedono ore;
- tabelle
*_tmpo replica rimaste dopo un reindex fallito.
L’architettura semplificata può essere rappresentata così:
DATI SORGENTE
│
▼
Modifica prodotto
│
▼
Trigger MySQL
│
▼
Changelog *_cl
version_id + entity_id
│
▼
MView State
ultimo version_id
│
▼
Cron / MView Update
│
▼
Indicizzazione incrementale
│
┌──────┴──────┐
│ │
▼ ▼
Dimensione A Dimensione B
│ │
└──────┬──────┘
▼
Calcolo dell'indice
│
▼
Replica / dati temporanei
dove previsto
│
▼
Table Switching
dove previsto
│
▼
INDICE LIVE
Il concetto più importante è che Magento separa tre responsabilità:
RILEVAMENTO MODIFICHE
↓
ELABORAZIONE
↓
PUBBLICAZIONE
Questa separazione è uno dei motivi per cui Magento può gestire cataloghi molto grandi senza dover ricalcolare tutti i dati derivati in maniera sincrona ogni volta che un amministratore salva un prodotto.
1. Perché Magento ha bisogno degli indici
Magento memorizza le informazioni del catalogo in una struttura fortemente normalizzata.
Il prezzo di un prodotto visualizzato sul frontend, ad esempio, può dipendere da:
- prezzo base;
- prezzo speciale;
- website;
- customer group;
- tier price;
- regole prezzo catalogo;
- tipo di prodotto;
- configurazione fiscale;
- relazioni dei prodotti configurabili;
- prezzi dei bundle;
- altre regole di business.
Calcolare dinamicamente tutto questo per ogni prodotto e per ogni richiesta frontend sarebbe estremamente costoso.
Magento trasforma quindi i dati sorgente in strutture ottimizzate per la lettura.
Concettualmente:
DATI DI BUSINESS NORMALIZZATI
│
│ calcoli costosi
▼
TABELLE INDICE
│
│ letture ottimizzate
▼
FRONTEND
Adobe definisce i dati originali come dictionary e la rappresentazione derivata come index.
La proprietà fondamentale di un indice è quindi:
Un indice è un dato derivato e, in linea di principio, ricostruibile.
Magento dovrebbe sempre essere in grado di rigenerare un indice partendo dai dati sorgente.
Questa distinzione è estremamente importante durante il troubleshooting.
Le tabelle contenenti le entità del catalogo rappresentano dati di business.
Le tabelle degli indici contengono dati derivati.
Le tabelle changelog e MView contengono invece le informazioni necessarie a mantenere sincronizzati i dati derivati con i dati sorgente.
Non vanno quindi considerate equivalenti.
2. Full Reindex vs Partial Reindex
Magento supporta due modalità concettualmente molto diverse di indicizzazione.
Full reindex
Un full reindex ricostruisce completamente un indice.
Ad esempio:
bin/magento indexer:reindex catalog_product_price
Concettualmente:
TUTTI I PRODOTTI
│
▼
CALCOLO DEI PREZZI
│
▼
INDICE PREZZI COMPLETO
Su cataloghi molto grandi questa operazione può essere estremamente costosa.
Se un ecommerce contiene centinaia di migliaia o milioni di SKU, sarebbe chiaramente inefficiente ricalcolare l’intero indice perché è cambiato un solo prodotto.
Per questo Magento supporta anche l’indicizzazione parziale.
Partial reindex
L’indicizzazione parziale elabora soltanto le entità interessate da una modifica.
Concettualmente:
Prodotto #42 modificato
│
▼
Changelog: 42
│
▼
L'indexer elabora 42
│
▼
Indice aggiornato
È qui che entra in gioco l’architettura MView di Magento.
3. Update on Save vs Update by Schedule
Gli indexer Magento possono generalmente funzionare in due modalità:
Update on Save
oppure:
Update by Schedule
La configurazione attuale può essere verificata con:
bin/magento indexer:show-mode
e modificata con:
bin/magento indexer:set-mode schedule catalog_product_price
oppure:
bin/magento indexer:set-mode realtime catalog_product_price
Update on Save
Con Update on Save, l’attività di indicizzazione viene attivata in seguito alla modifica dei dati applicativi.
Questo permette di rendere rapidamente disponibili le modifiche nell’indice, ma sposta una parte maggiore del carico computazionale verso le operazioni di scrittura.
Su installazioni molto trafficate può aumentare:
- latenza dei salvataggi;
- lock contention;
- indicizzazioni concorrenti;
- carico sul database;
- probabilità di deadlock.
Update by Schedule
Con Update by Schedule, Magento registra ciò che è cambiato e lascia al cron il compito di elaborare le modifiche in maniera asincrona.
Concettualmente:
RICHIESTA HTTP
│
▼
SALVATAGGIO PRODOTTO
│
▼
REGISTRA MODIFICA
│
└────────────► FINE RICHIESTA
Successivamente...
CRON
│
▼
LEGGE LE MODIFICHE
│
▼
AGGIORNA L'INDICE
Negli ambienti di produzione l’indicizzazione schedulata è generalmente l’architettura preferibile.
Le linee guida Adobe sulle performance raccomandano infatti l’indicizzazione schedulata perché separa le scritture sul catalogo dalle operazioni potenzialmente costose di indicizzazione.
4. MView: l’astrazione Materialized View di Magento
Magento chiama il proprio sistema di change tracking MView, abbreviazione di Materialized View.
MySQL non offre lo stesso meccanismo nativo di materialized view disponibile in alcuni altri database, quindi Magento implementa una propria astrazione.
Il componente framework responsabile è:
Magento\Framework\Mview
Un modulo definisce le tabelle da monitorare all’interno di:
etc/mview.xml
Un esempio semplificato può essere:
<view
id="catalog_category_product"
class="Magento\Catalog\Model\Indexer\Category\Product"
group="indexer">
<subscriptions>
<table
name="catalog_category_entity"
entity_column="entity_id"
/>
<table
name="catalog_category_entity_int"
entity_column="entity_id"
/>
</subscriptions>
</view>
La configurazione dice sostanzialmente a Magento:
Monitora queste tabelle sorgente
↓
Quando vengono modificate
↓
Registra l'entità interessata
↓
Invia gli entity ID
↓
A questo indexer
Questo rappresenta il collegamento tra le modifiche al database e l’indicizzazione incrementale.
5. Trigger MySQL: rilevare le modifiche
Quando una subscription MView è attiva, Magento utilizza trigger database per rilevare le modifiche alle tabelle sottoscritte.
A seconda della subscription, i trigger possono reagire a operazioni quali:
INSERT
UPDATE
DELETE
Il punto fondamentale è che il trigger non esegue normalmente il calcolo costoso dell’indice.
Il suo compito è molto più semplice:
MODIFICA DATI
│
▼
TRIGGER MYSQL
│
▼
REGISTRA ENTITY ID
Questo è fondamentale per le performance.
Immaginiamo di modificare il prezzo di un prodotto.
Una cattiva architettura potrebbe fare:
UPDATE PRODOTTO
↓
Calcolo prezzi per customer group
↓
Calcolo website
↓
Calcolo regole
↓
Aggiornamento tabelle indice
↓
Risposta HTTP
L’architettura schedulata di Magento mira invece a fare:
UPDATE PRODOTTO
↓
INSERT ID NEL CHANGELOG
↓
RISPOSTA
Il lavoro costoso viene eseguito successivamente e in maniera asincrona.
6. Le tabelle changelog *_cl
Le modifiche rilevate da MView vengono memorizzate in tabelle di changelog.
I loro nomi generalmente terminano con:
_cl
Una tabella changelog concettuale potrebbe essere:
catalog_product_price_cl
+------------+-----------+
| version_id | entity_id |
+------------+-----------+
| 10451 | 42 |
| 10452 | 84 |
| 10453 | 42 |
| 10454 | 120 |
+------------+-----------+
Le colonne più importanti sono:
version_id
entity_id
entity_id
Identifica l’entità interessata dalla modifica.
Ad esempio:
42
può rappresentare il prodotto con ID 42.
version_id
version_id fornisce una sequenza ordinata delle entry del changelog.
Concettualmente si comporta in modo simile all’offset di uno stream di eventi:
10451
10452
10453
10454
...
Questo permette a Magento di determinare:
Quali modifiche sono già state elaborate dall’indexer?
È uno dei concetti chiave dell’indicizzazione incrementale.
7. mview_state: il cursore del consumer
Magento deve ricordare fino a quale punto ogni MView ha elaborato il proprio changelog.
Questa informazione viene memorizzata nella tabella:
mview_state
Concettualmente:
+-----------------------+--------+------------+
| view_id | status | version_id |
+-----------------------+--------+------------+
| catalog_product_price | idle | 10452 |
+-----------------------+--------+------------+
Il valore più interessante è:
version_id
che rappresenta la posizione già elaborata dalla view.
Possiamo considerarlo come un consumer cursor.
Supponiamo che:
mview_state.version_id = 10452
mentre:
MAX(catalog_product_price_cl.version_id) = 10520
Magento sa che esistono ancora modifiche non elaborate.
Concettualmente:
10452 10520
│ │
▼ ▼
ULTIMO ELABORATO ULTIMA MODIFICA
|-----------------------------|
INDEXER LAG
8. Come funziona l’elaborazione incrementale
L’SQL esatto e la gestione dei batch sono dettagli implementativi che possono variare, ma concettualmente il consumer MView esegue qualcosa di equivalente a:
SELECT DISTINCT entity_id
FROM catalog_product_price_cl
WHERE version_id > :last_processed_version
AND version_id <= :current_upper_bound;
Supponiamo:
ultima versione elaborata = 1000
massimo attuale changelog = 1100
L’indexer elabora:
1001 → 1100
Al termine dell’elaborazione, se tutto va a buon fine, il cursore può avanzare.
mview_state.version_id
1000
↓
1100
Il modello ricorda molto:
Producer
↓
Log ordinato
↓
Consumer
↓
Consumer Offset
Questa analogia è particolarmente utile per comprendere MView.
9. Perché è importante avere un limite superiore
Un problema interessante si presenta quando nuove modifiche al catalogo avvengono mentre l’indexer sta già lavorando.
Immaginiamo:
L'indexer parte
ultima versione = 1100
Durante l’elaborazione viene salvato un altro prodotto:
nuova versione = 1101
Magento deve evitare di includere continuamente nuove modifiche all’interno di un insieme di dati che cambia mentre lo sta elaborando.
Concettualmente lavora quindi su un intervallo delimitato:
ULTIMO CURSORE LIMITE DEL BATCH
│ │
▼ ▼
1000 ----------------------- 1100
1101
│
▼
CICLO SUCCESSIVO
In questo modo il batch corrente è stabile.
Le modifiche arrivate dopo il limite vengono mantenute nel changelog e saranno elaborate nel ciclo MView successivo.
Questo contribuisce a rendere affidabile l’elaborazione asincrona incrementale.
10. Gli entity_id duplicati sono normali
Un prodotto può essere modificato più volte prima che l’indexer venga eseguito.
Ad esempio:
version_id entity_id
2001 42
2002 42
2003 42
2004 73
Questo non significa necessariamente che Magento debba ricalcolare tre volte il prodotto 42.
L’informazione utile è generalmente:
42 è cambiato
73 è cambiato
per questo l’elaborazione può lavorare sull’insieme distinto degli identificativi.
Concettualmente:
CHANGELOG
42
42
42
73
↓ DISTINCT
42
73
↓
INDEXER
Questo spiega anche perché il numero di righe presenti nel changelog non deve essere automaticamente interpretato come il numero di prodotti in attesa di indicizzazione.
11. Magento elabora le modifiche a batch
Backlog molto grandi non possono essere caricati in memoria PHP tutti insieme in maniera sicura.
Immaginiamo un’integrazione che generi:
500.000 aggiornamenti prodotto
Elaborare ogni entità all’interno di un singolo array PHP potrebbe produrre:
elevato consumo memoria
↓
OOM
↓
processo PHP terminato
↓
fallimento indexer
Magento supporta quindi l’elaborazione a batch.
Concettualmente:
CHANGELOG
1 ─────────────────── 500.000
│
▼
Batch 1
│
▼
Batch 2
│
▼
Batch 3
│
▼
...
La dimensione dei batch diventa quindi un importante parametro di performance.
Batch troppo piccoli:
più query
più overhead
più transazioni
Batch troppo grandi:
più memoria
lock più lunghi
transazioni più grandi
rischio OOM
Trovare un buon equilibrio è particolarmente importante per:
- cataloghi molto grandi;
- importazioni ERP;
- sincronizzazioni PIM;
- feed marketplace;
- aggiornamenti massivi dei prezzi.
12. Dimensions: un prodotto non significa un solo valore indicizzato
Una delle parti più sottovalutate dell’indicizzazione Magento è l’indicizzazione dimensionale.
Alcuni valori derivati dipendono dal contesto.
Il prezzo di un prodotto, per esempio, può dipendere da:
Prodotto
×
Website
×
Customer Group
Concettualmente:
Prodotto 42
Website A
/ \
Retail Wholesale
Website B
/ \
Retail Wholesale
La modifica di un solo prodotto può quindi generare più record derivati nell’indice.
Questo spiega perché la complessità dell’indicizzazione non cresce necessariamente soltanto in funzione del:
numero di prodotti
ma può crescere più similmente a:
Prodotti
× Website
× Customer Group
× Altre dimensioni
a seconda dell’indexer.
Nelle installazioni B2B o multi-website di grandi dimensioni, questa differenza può diventare enorme.
13. Perché l’indicizzazione può esplodere nei Magento multi-website
Consideriamo un’installazione con:
500.000 prodotti
8 website
6 customer group
Un modello mentale semplicistico considera soltanto:
500.000 prodotti
Ma un calcolo dei prezzi sensibile alle dimensioni può coinvolgere uno spazio teorico molto più ampio:
500.000 × 8 × 6
ovvero:
24.000.000 di combinazioni
prodotto/website/customer-group
Questo non significa che Magento generi necessariamente e sempre esattamente 24 milioni di righe per ogni indexer.
Significa però che il numero di prodotti da solo non basta per stimare il costo dell’indicizzazione.
L’architettura conta.
Quando si analizza un problema di indicizzazione lenta, bisogna considerare sempre:
numero SKU
+
numero website
+
customer group
+
tipi di prodotto
+
catalog rule
+
architettura inventory
+
custom indexer
14. Tabelle replica e Table Switching
Alcuni indexer Magento utilizzano una strategia in cui i nuovi dati vengono preparati separatamente dalla tabella attualmente utilizzata dal frontend.
Concettualmente:
TABELLA LIVE
catalog_product_index_price
TABELLA REPLICA
catalog_product_index_price_replica
Il processo può quindi diventare:
Frontend
│
▼
INDICE LIVE
Nel frattempo...
Dati sorgente
│
▼
Indexer
│
▼
REPLICA
Quando i nuovi dati sono pronti, Magento può eseguire uno switch tra le tabelle invece di sostituire progressivamente un grande insieme di dati live.
Concettualmente:
PRIMA
live → DATI VECCHI
replica → DATI NUOVI
SWITCH
DOPO
live → DATI NUOVI
Il vantaggio è evidente.
Il frontend non deve assistere a un indice che viene ricostruito riga dopo riga.
La pubblicazione diventa invece un’operazione relativamente breve, dopo che la parte computazionalmente costosa è già stata eseguita.
Una precisazione importante
Replica table e table switching non sono passaggi universali implementati allo stesso modo da tutti gli indexer Magento.
I diversi indexer possono avere implementazioni differenti.
Di conseguenza, diagrammi come:
MView → Replica → Swap
sono utili come modello architetturale, ma non devono essere interpretati come se ogni indexer core o custom creasse necessariamente una tabella *_replica.
Per analizzare un indexer specifico è sempre opportuno verificarne direttamente l’implementazione.
15. Cosa sono le tabelle *_tmp?
Durante l’indicizzazione è possibile trovare tabelle con nomi contenenti:
_tmp
o altre convenzioni utilizzate per indicare tabelle temporanee o di staging.
Possono essere utilizzate per operazioni intermedie durante il processo di indicizzazione.
Questo porta talvolta a una pratica di troubleshooting molto pericolosa:
"Questa sembra una tabella temporanea.
Facciamo DROP."
Non fatelo.
Durante un’indicizzazione attiva:
DROP *_tmp
può interferire con il processo in esecuzione.
La tabella potrebbe essere necessaria per:
- calcoli intermedi;
- aggregazioni;
- table switching;
- scritture in staging;
- elaborazione a batch.
16. Quando possono essere eliminate le tabelle temporanee?
Un processo fallito può lasciare artefatti temporanei nel database.
Le cause tipiche includono:
PHP OOM
SIGKILL
restart del container
riavvio del server
interruzione del deployment
errore database
terminazione manuale del processo
Potremmo quindi trovare:
some_index_tmp
molto tempo dopo che il processo di indicizzazione sembra essersi concluso.
Prima di eliminare qualsiasi cosa è necessario verificare:
1. Nessun processo indexer è attivo
2. Il cron non sta elaborando quell'indice
3. Nessuna sessione DB sta usando la tabella
4. La tabella è realmente temporanea o abbandonata
5. L'indexer è in grado di ricrearla
6. Se necessario, è possibile eseguire successivamente un full reindex
Solo a questo punto può essere presa in considerazione una pulizia manuale.
17. Tabelle che NON dovresti eliminare con leggerezza
Due componenti particolarmente importanti sono:
*_cl
e:
mview_state
Non sono normali tabelle temporanee.
Fanno parte dell’infrastruttura di change tracking utilizzata dall’indicizzazione incrementale.
Concettualmente:
DATI SORGENTE
│
▼
*_cl
│
▼
mview_state
│
▼
INDEXER
Rimuoverle o modificarle senza comprenderne le conseguenze può rompere la relazione tra:
ciò che è cambiato
e:
ciò che Magento ritiene
di aver già elaborato
Il risultato può essere molto più difficile da diagnosticare rispetto a una semplice tabella temporanea rimasta nel database.
18. indexer_state vs mview_state
Queste due tabelle vengono spesso confuse.
In realtà risolvono problemi differenti.
indexer_state
indexer_state descrive lo stato dell’indexer.
Gli stati concettualmente più importanti includono:
valid
invalid
working
Permette di rispondere a domande come:
Questo indice è considerato valido?
oppure:
Magento lo sta attualmente elaborando?
mview_state
mview_state descrive invece l’avanzamento del consumer della Materialized View.
Risponde alla domanda:
Fino a quale posizione del changelog questa MView è arrivata?
Concettualmente:
indexer_state
│
└── L'indice/indexer è valido o in esecuzione?
mview_state
│
└── Fino a dove abbiamo elaborato il changelog?
Durante il troubleshooting dell’indicizzazione schedulata, controllare soltanto indexer_state è quindi spesso insufficiente.
19. Misurare l’Indexer Lag
Una metrica operativa estremamente utile può essere ricavata confrontando il changelog con il cursore MView.
Supponiamo:
SELECT MAX(version_id)
FROM catalog_product_price_cl;
restituisca:
850000
mentre:
SELECT version_id
FROM mview_state
WHERE view_id = 'catalog_product_price';
restituisca:
845000
Il gap tra le versioni è:
850000 - 845000 = 5000
Concettualmente:
CURSORE MVIEW TESTA CHANGELOG
845000 ─────────────────────────── 850000
5000 versioni
Questo valore può essere utilizzato come indicatore del fatto che il consumer stia iniziando a rimanere indietro.
Ma bisogna fare una precisazione importante.
Il version gap non equivale al numero di prodotti
Perché:
un singolo prodotto
può apparire più volte nel changelog.
Quindi:
version gap = 5000
non significa necessariamente:
5000 prodotti in attesa
Un sistema di monitoring più sofisticato dovrebbe misurare diversi indicatori:
version lag
numero di entity distinte in backlog
time lag
velocità di elaborazione
durata esecuzioni cron
Insieme, forniscono una visione molto migliore dello stato dell’indicizzazione.
20. Una strategia migliore per monitorare l’indicizzazione
Il monitoring di produzione dovrebbe idealmente rispondere alla domanda:
Quanto è indietro l'indexer?
e non soltanto:
L'ultimo cron ha avuto successo?
Metriche utili includono:
MAX(*_cl.version_id)
mview_state.version_id
COUNT(DISTINCT pending entity_id)
indexer_state.status
durata cron
throughput indicizzazione
età della modifica non elaborata più vecchia
Questo permette di generare alert prima che merchant o clienti inizino a vedere dati obsoleti.
Ad esempio:
NORMALE
Changelog head 105000
MView cursor 104995
Lag 5
WARNING
Changelog head 205000
MView cursor 170000
Lag 35000
Un gap che cresce continuamente è molto più significativo di un gap elevato ma temporaneo.
Se:
velocità produzione > velocità consumo
allora:
backlog → continua a crescere
Concettualmente l’indexer Magento è diventato un consumer sovraccarico.
21. Perché bin/magento indexer:reindex non è sempre la soluzione corretta
Una reazione molto comune quando Magento mostra dati non aggiornati è:
bin/magento indexer:reindex
A volte risolve il problema.
Ma potrebbe risolvere soltanto il sintomo.
Supponiamo che il vero problema sia:
cron non funzionante
oppure:
backlog MView
oppure:
custom indexer che impiega 20 minuti
oppure:
OOM durante l'elaborazione schedulata
Un full reindex manuale potrebbe temporaneamente ripristinare la correttezza dei dati.
Ma:
30 minuti dopo
il problema ricompare.
Una sequenza diagnostica migliore è:
bin/magento indexer:status
bin/magento indexer:show-mode
bin/magento cron:run
e successivamente analizzare:
cron_schedule
indexer_state
mview_state
*_cl
log applicativi
memoria PHP
attività database
L’obiettivo non deve essere semplicemente ricostruire l’indice.
Bisogna capire perché la sincronizzazione incrementale non riesce più a mantenere il passo.
22. Scenario comune: il consumer dell’indexer rimane indietro
Consideriamo un PIM che produce:
100.000 modifiche ogni 5 minuti
mentre l’indexer Magento riesce a processarne solamente:
60.000 ogni 5 minuti
Il sistema si comporta così:
Modifiche in ingresso
100k / 5 min
│
▼
CHANGELOG
│
│ 60k / 5 min
▼
INDEXER
Ogni ciclo lascia quindi:
40.000
nuove modifiche in backlog.
Dopo un’ora:
40.000 × 12
=
480.000
modifiche possono essersi accumulate.
L’indexer non è necessariamente “rotto”.
Semplicemente non possiede abbastanza throughput.
Questa distinzione è fondamentale.
La soluzione potrebbe richiedere:
- riduzione dei picchi di importazione;
- tuning dei batch;
- ottimizzazione delle query dei custom indexer;
- riduzione delle scritture inutili;
- revisione delle dimensioni;
- miglioramento delle performance del database;
- distribuzione dei workload;
- redesign delle integrazioni custom.
Eseguire più full reindex non risolve un problema di throughput.
23. Il costo nascosto dei salvataggi prodotto inutili
Le integrazioni di terze parti generano spesso aggiornamenti non necessari.
Ad esempio, un ERP potrebbe eseguire:
UPDATE prodotto
anche quando il valore non è realmente cambiato.
Dal punto di vista del business:
non è cambiato nulla
ma dal punto di vista del database e del change detection quella scrittura può comunque generare lavoro downstream, a seconda delle tabelle interessate e del comportamento dei trigger.
Su larga scala:
ERP
│
├── Prodotto 1 invariato → UPDATE
├── Prodotto 2 invariato → UPDATE
├── Prodotto 3 invariato → UPDATE
└── Prodotto 4 invariato → UPDATE
può generare un enorme carico inutile.
Per grandi installazioni Magento, una delle migliori ottimizzazioni dell’indicizzazione è spesso sorprendentemente semplice:
Non scrivere dati che non sono cambiati.
Questo riduce:
scritture database
esecuzioni trigger
crescita changelog
carico indexer
cache invalidation
lock contention
ancora prima che l’indexer inizi a lavorare.
24. Cron fa parte dell’architettura di indicizzazione
Con l’indicizzazione schedulata, cron non è semplicemente un dettaglio operativo.
Fa parte del modello di consistenza.
L’architettura è effettivamente:
Scrittura Magento
│
▼
Changelog
│
▼
Cron
│
▼
MView
│
▼
Indexer
Se il cron si ferma:
Magento continua ad accettare scritture
ma:
gli indici smettono di aggiornarsi
Si crea quindi una modalità di errore interessante:
Dati Admin = corretti
Dati sorgente database = corretti
Indice frontend = obsoleto
L’applicazione ecommerce può quindi sembrare perfettamente funzionante mentre gli indici divergono progressivamente dai dati sorgente.
25. Perché questa architettura scala
La vera forza dell’indicizzazione Magento non è semplicemente il fatto che esistano degli indexer.
È la separazione tra:
WRITE PATH
e:
ELABORAZIONE DEI DATI DERIVATI
Un aggiornamento prodotto può rimanere relativamente leggero:
Salvataggio prodotto
↓
Modifica database
↓
Trigger
↓
Changelog
↓
Fine richiesta
mentre i calcoli costosi vengono eseguiti separatamente:
Cron
↓
MView
↓
Batch
↓
Dimensioni
↓
Indexer
↓
Tabelle indice
e, quando supportato:
Build
↓
Replica
↓
Switch
↓
Publish
Questa architettura permette a Magento di spostare i calcoli più costosi fuori dalle richieste interattive.
26. Magento Indexing come sistema Event-Driven
Un modo interessante per comprendere l’indicizzazione Magento consiste nello smettere di pensarla come un semplice “comando di reindex”.
Dal punto di vista architetturale, l’indicizzazione schedulata ricorda una pipeline semplificata di elaborazione eventi.
MODIFICA DATABASE
│
▼
TRIGGER
│
▼
CHANGELOG
│
▼
CURSORE
│
▼
CONSUMER
│
▼
STATO DERIVATO
L’analogia con sistemi come Kafka non è perfetta, ma è estremamente utile:
| Concetto Event-Driven | Concetto Magento |
|---|---|
| Producer | Scrittura catalogo/database |
| Event capture | Trigger MySQL |
| Event log | *_cl |
| Sequence / offset | version_id |
| Consumer offset | mview_state.version_id |
| Consumer | Indexer |
| Projection | Tabella indice |
Questo modello mentale rende molti problemi di indicizzazione Magento molto più semplici da analizzare.
Invece di chiedersi:
Perché il reindex non funziona?
è meglio chiedersi:
Le modifiche vengono prodotte?
↓
Entrano nel changelog?
↓
Il consumer viene eseguito?
↓
Il cursore avanza?
↓
Il consumer elabora più velocemente
di quanto vengano prodotte modifiche?
↓
L'indice risultante viene pubblicato correttamente?
Questo è un modello di debugging molto più potente.
27. Checklist pratica per il troubleshooting
Quando Magento mostra dati indicizzati non aggiornati, conviene analizzare la pipeline da sinistra verso destra.
SORGENTE
↓
RILEVAMENTO MODIFICHE
↓
CHANGELOG
↓
CONSUMER
↓
INDICE
↓
FRONTEND
Partiamo da:
bin/magento indexer:status
Poi:
bin/magento indexer:show-mode
Verifichiamo il cron.
Controlliamo:
cron_schedule
Successivamente:
indexer_state
mview_state
Confrontiamo:
MAX(*_cl.version_id)
con:
mview_state.version_id
Verifichiamo se il cursore avanza tra un’esecuzione cron e la successiva.
Successivamente analizziamo:
errori PHP
OOM kill
lock MySQL
deadlock
slow query
transazioni lunghe
disk I/O
CPU saturation
custom indexer
Soltanto dopo aver compreso la causa del problema un full reindex dovrebbe essere considerato una soluzione e non semplicemente un’operazione temporanea di recovery.
28. Il quadro completo
L’architettura può essere rappresentata in maniera più completa così:
WRITE PATH
Salvataggio prodotto
│
▼
Tabelle sorgente
│
▼
Trigger MySQL
│
▼
Changelog *_cl
│
│
│ confine asincrono
▼
PROCESSING PATH
Cron
│
▼
MView
│
▼
mview_state
Cursor
│
▼
Entity ID pendenti
│
▼
Batch
│
▼
Indexer
│
▼
Dimensions
│
▼
Calcolo dell'indice
│
▼
PUBBLICAZIONE
Tabelle indice
│
dove supportato
▼
Replica / Swap
│
▼
INDICE LIVE
│
▼
FRONTEND
Il concetto chiave è questo:
L’indicizzazione Magento non è principalmente un meccanismo di rebuild. È una pipeline di sincronizzazione che mantiene proiezioni ottimizzate dei dati di business normalizzati.
Una volta compreso questo concetto, il comando:
bin/magento indexer:reindex
diventa soltanto una piccola parte del sistema.
La vera architettura è:
Rilevamento modifiche
↓
Changelog
↓
Consumer Cursor
↓
Elaborazione incrementale
↓
Calcolo dimensionale
↓
Pubblicazione sicura
Ed è proprio questa separazione che permette ad Adobe Commerce e Magento 2 di eseguire in modo asincrono calcoli di catalogo costosi mantenendo allo stesso tempo rapide le letture sul frontend.
FAQ
Cos’è un indice in Magento 2?
Un indice è una rappresentazione derivata dei dati di business Magento, ottimizzata per la lettura.
Invece di calcolare informazioni complesse come prezzi, regole o associazioni di categoria durante ogni richiesta frontend, Magento esegue questi calcoli in anticipo e memorizza il risultato nelle tabelle indice.
Cos’è MView in Magento 2?
MView significa Materialized View.
Magento utilizza il framework Magento\Framework\Mview per tracciare le modifiche alle entità del database e aggiornare incrementalmente gli indici.
Il sistema utilizza subscription sulle tabelle, trigger database, changelog e una posizione di avanzamento memorizzata per determinare cosa è cambiato dal ciclo di indicizzazione precedente.
Cosa sono le tabelle Magento *_cl?
Le tabelle che terminano con _cl sono changelog associati alle subscription MView.
Generalmente contengono almeno informazioni equivalenti a:
version_id
entity_id
Permettono a Magento di identificare quali entità sono state modificate e devono essere elaborate incrementalmente.
Cos’è version_id?
version_id è l’identificativo ordinato delle entry presenti nel changelog.
Permette a Magento di determinare la posizione delle modifiche e confrontarla con quella già elaborata dalla MView.
Può essere considerato concettualmente simile all’offset di uno stream di eventi.
Cosa significa mview_state.version_id?
Rappresenta l’avanzamento di una MView nel proprio changelog.
Se il changelog ha raggiunto:
20000
mentre lo stato MView è:
15000
esiste ancora lavoro non elaborato.
La differenza può essere utilizzata come indicatore operativo del lag dell’indexer, anche se non equivale direttamente al numero di prodotti in attesa.
Qual è la differenza tra indexer_state e mview_state?
indexer_state descrive lo stato operativo e di validità dell’indexer.
mview_state tiene invece traccia dell’avanzamento della MView nel changelog.
Semplificando:
indexer_state → "L'indice/indexer è OK?"
mview_state → "Fino a dove abbiamo elaborato?"
Perché Magento utilizza trigger MySQL per l’indicizzazione schedulata?
I trigger permettono a Magento di rilevare rapidamente le modifiche al database senza eseguire il calcolo costoso dell’indice all’interno della richiesta che ha modificato i dati.
Il trigger registra l’entità interessata nel changelog e l’indicizzazione viene successivamente eseguita in maniera asincrona.
Magento reindicizza tutto il catalogo quando cambia un solo prodotto?
Normalmente no, quando l’indicizzazione incrementale funziona correttamente.
MView registra le entità interessate e l’indexer può elaborare soltanto quelle.
Un full reindex ricostruisce invece completamente l’indice.
Tutti gli indexer Magento utilizzano tabelle replica?
No.
Le tabelle replica, le tabelle di staging e il table switching sono strategie implementative utilizzate da specifici indexer.
Non bisogna considerarle una caratteristica universale di tutti gli indexer Magento.
Per analizzare un indexer specifico è necessario verificarne l’implementazione.
Posso eliminare le tabelle Magento *_tmp?
Non mentre un indexer potrebbe utilizzarle.
Le tabelle temporanee possono rimanere dopo processi falliti o interrotti, ma dovrebbero essere eliminate soltanto dopo aver verificato che nessun processo di indicizzazione attivo ne dipenda e dopo aver compreso come vengono utilizzate dall’indexer interessato.
Posso eliminare le tabelle Magento *_cl?
Non dovrebbero mai essere eliminate con leggerezza.
Le tabelle changelog fanno parte dell’architettura di indicizzazione incrementale.
Modificarle o eliminarle in modo errato può far perdere a Magento la traccia delle modifiche che devono ancora essere elaborate.
Perché le tabelle Magento *_cl continuano a crescere?
Le cause più comuni includono:
- cron non funzionante;
- errori nel consumer MView;
- indexer più lento rispetto alla velocità con cui vengono generate modifiche;
- importazioni molto grandi;
- aggiornamenti prodotto eccessivi;
- custom indexer inefficienti;
- problemi di performance del database.
Uno dei primi controlli da effettuare è confrontare la testa del changelog con mview_state.version_id.
Una tabella *_cl molto grande indica necessariamente un problema?
Non necessariamente.
La domanda più importante è capire se il cursore MView sta avanzando e se il backlog cresce continuamente.
Un grande backlog che viene rapidamente consumato può essere perfettamente normale dopo una grossa importazione.
Un backlog più piccolo ma in continua crescita potrebbe invece indicare un problema strutturale di throughput.
Perché un indexer:reindex manuale risolve il problema soltanto temporaneamente?
Perché il full reindex può correggere i dati derivati senza risolvere il problema che impedisce all’indicizzazione incrementale di funzionare.
Ad esempio, il cron potrebbe continuare a essere fermo oppure il consumer MView potrebbe non riuscire a mantenere il passo.
Quando arrivano nuove modifiche al catalogo, l’indice torna nuovamente obsoleto.
Perché l’indicizzazione Magento può essere lenta anche con relativamente pochi prodotti?
Il numero dei prodotti è soltanto uno dei fattori.
La complessità può dipendere anche da:
website
customer group
catalog rule
tipi di prodotto
inventory source
custom indexer
Un’installazione B2B multi-website può quindi richiedere molto più lavoro di indicizzazione rispetto a un altro ecommerce con lo stesso numero di SKU.
In produzione è meglio Update on Save o Update by Schedule?
Per la maggior parte dei workload di produzione, Adobe raccomanda Update by Schedule.
Questa modalità disaccoppia le operazioni di scrittura dall’indicizzazione e riduce il rischio che calcoli costosi o lock interferiscano con operazioni Admin e integrazioni.
La scelta può comunque dipendere dal workload e dal singolo indexer.
Come posso monitorare preventivamente l’indicizzazione Magento?
Come minimo è utile monitorare:
indexer_state.status
mview_state.version_id
MAX(*_cl.version_id)
esecuzioni cron
durata indexer
Per installazioni di grandi dimensioni può essere utile monitorare anche:
numero di entity distinte pendenti
età della modifica più vecchia non elaborata
throughput
velocità di crescita del backlog
database lock
utilizzo memoria PHP
Una differenza tra produzione e consumo del changelog che cresce continuamente è un forte indicatore del fatto che la pipeline di indicizzazione stia rimanendo indietro.
Fonti e approfondimenti
Adobe Commerce Developer Documentation
Architettura dell’indicizzazione
La documentazione Adobe descrive l’architettura di indicizzazione di Magento, compresi full indexing, partial indexing, MView, configurazione degli indexer e modalità di aggiornamento.
https://developer.adobe.com/commerce/php/development/components/indexing/
Creazione di custom indexer
Particolarmente utile per comprendere indexer.xml, mview.xml, executeRow(), executeList() ed executeFull().
https://developer.adobe.com/commerce/php/development/components/indexing/custom-indexer
Architettura del framework Adobe Commerce
Documentazione generale relativa al framework Commerce e ai suoi principali componenti architetturali.
https://developer.adobe.com/commerce/php/architecture/framework
Adobe Commerce Operations Documentation
Gestione degli indexer da CLI
Riferimento ufficiale per comandi come:
bin/magento indexer:status
bin/magento indexer:show-mode
bin/magento indexer:set-mode
bin/magento indexer:reindex
Index Management
Documentazione ufficiale relativa a Update on Save, Update by Schedule e gestione dello stato degli indexer.
https://experienceleague.adobe.com/en/docs/commerce-admin/systems/tools/index-management
Best practice per la configurazione degli indexer
Adobe raccomanda l’indicizzazione schedulata per i workload di produzione e analizza le implicazioni della configurazione degli indexer sulle performance.
Codice sorgente Magento 2
Per comprendere l’architettura a un livello ancora più profondo, il codice sorgente Magento rimane il riferimento definitivo.
Le aree particolarmente interessanti includono:
Magento\Framework\Mview
Magento\Framework\Indexer
Magento\Indexer
Magento\Catalog\Model\Indexer
Tra le classi e i componenti più utili da analizzare troviamo:
Magento\Framework\Mview\View
Magento\Framework\Mview\View\Subscription
Magento\Framework\Mview\View\Changelog
Magento\Framework\Indexer
Il codice sorgente è disponibile nel repository ufficiale Magento Open Source:
https://github.com/magento/magento2
Quando si analizza un indexer specifico è sempre consigliabile verificarne direttamente l’implementazione, perché non tutti gli indexer utilizzano esattamente la stessa strategia per tabelle temporanee, dimensioni, replica e table switching.
