Magento 2 MSI et la table inventory_stock_2 – le tueur silencieux des performances d’indexation
Dans l’un de nos récents déploiements Magento 2 (environ 200 000 produits, Multi Source Inventory activé), nous avons constaté des temps d’indexation très longs pour les modules :
- Inventory
- Product Price
- Category Products
Tous les index classiques semblaient corrects. MariaDB disposait de 256 Go de RAM, le buffer pool était cohérent, les index EAV étaient en place, et malgré cela des requêtes lourdes liées à
inventory_stock_2
continuaient d’apparaître. Après une analyse plus poussée, nous avons compris que le problème ne venait pas du volume de données lui-même, mais de la manière dont Magento MSI crée la table inventory_stock_*.
D’où vient la table inventory_stock_2 ?
La table inventory_stock_2 apparaît dans Magento après l’activation de
Multi Source Inventory (MSI).
Magento crée une table distincte pour chaque stock :
- inventory_stock_1
- inventory_stock_2
- inventory_stock_3 ...
La table est créée dynamiquement par la classe :
Magento\InventoryIndexer\Indexer\IndexStructure
et ressemble plus ou moins à ceci :
CREATE TABLE inventory_stock_2 (
sku varchar(64) NOT NULL,
quantity decimal(12,4) NOT NULL DEFAULT 0.0000,
is_salable tinyint(1) NOT NULL,
PRIMARY KEY (sku),
KEY index_sku_qty (sku, quantity)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
Où se situe le problème ?
Il existe deux problèmes majeurs :
1. Absence de utf8mb4
La colonne sku est souvent créée en utf8_general_ci,
alors que dans catalog_product_entity nous avons :
utf8mb4_general_ci
Cela provoque :
- une conversion de caractères lors des JOIN
- l’utilisation du join buffer
- de moins bons plans d’exécution
2. Aucun index commençant par is_salable
Magento exécute très souvent des requêtes du type :
WHERE stock_index.is_salable = 0
OR stock_index.is_salable IS NULL
Et comme le seul index existant est :
(sku, quantity)
MariaDB ne peut pas filtrer efficacement sur is_salable.
Résultat :
- scans complets de table
- DELETE longs lors du price index
- lock wait timeout 1205
- inventory indexer bloqué en "processing"
Pourquoi un ALTER TABLE manuel ne fonctionne-t-il pas ?
Parce qu’à chaque indexation Magento effectue :
- DROP TABLE inventory_stock_X
- CREATE TABLE inventory_stock_X
Toute modification manuelle disparaît alors immédiatement.
La solution doit donc être : modifier automatiquement la table juste après sa création.
Solution : le module Kowal_MsiStockFix
Au lieu de modifier le core Magento, nous créons un plugin qui, après création de la table :
- définit utf8mb4_general_ci
- ajoute l’index (is_salable, sku)
Plugin : IndexStructurePlugin.php
<?php
declare(strict_types=1);
namespace Kowal\MsiStockFix\Plugin;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\InventoryIndexer\Indexer\IndexStructure;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexName;
use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexNameResolverInterface;
class IndexStructurePlugin
{
private const CHARSET = 'utf8mb4';
private const COLLATION = 'utf8mb4_general_ci';
public function __construct(
private readonly ResourceConnection $resource,
private readonly IndexNameResolverInterface $resolver
) {}
public function afterCreate(IndexStructure $subject, $result, IndexName $indexName, string $connectionName)
{
$connection = $this->resource->getConnection($connectionName);
$resolvedName = $this->resolver->resolveName($indexName);
$table = $this->resource->getTableName($resolvedName);
if (strpos($table, 'inventory_stock_') === false) {
return $result;
}
if (!$connection->isTableExists($table)) {
return $result;
}
$connection->query(
sprintf(
"ALTER TABLE `%s` CONVERT TO CHARACTER SET %s COLLATE %s",
$table,
self::CHARSET,
self::COLLATION
)
);
$indexes = $connection->getIndexList($table);
if (!isset($indexes['IDX_STOCK_SALABLE_SKU'])) {
$connection->addIndex(
$table,
'IDX_STOCK_SALABLE_SKU',
['is_salable', 'sku'],
AdapterInterface::INDEX_TYPE_INDEX
);
}
return $result;
}
}
Installation du module
bin/magento module:enable Kowal_MsiStockFix
bin/magento setup:upgrade
bin/magento cache:flush
bin/magento indexer:reindex inventory
Effets après mise en place
- La collation ne disparaît plus
- DELETE plus rapides sur le price index
- Plus de lock wait timeout
- L’index inventory se termine correctement
- Moins de join buffer et moins de full scans
Résumé
Le problème de la table inventory_stock_2 est peu évident,
car tout semble "correct" au premier regard et les index paraissent sains.
Seule l’analyse :
- des plans d’exécution
- de la collation
- du mécanisme de recréation des tables MSI
permet d’identifier la vraie cause.
Si vous avez une boutique avec beaucoup de produits et utilisez MSI, ce type de solution vaut clairement la peine.
Magento est souvent rapide. Il faut parfois simplement l’aider un peu.
