Découverte de la journée … un bug dans la version 1.3 de Magento lors de forte charge de mysql…. des commandes apparaissent avec des increment id identiques
La méthode fetchNewIncrementId de la classe Furet_Eav_Model_Entity_Type qui au final permet de récupérer le nouvel increment id, génère un SELECT et un UPDATE
SELECT `eav_entity_store`.* FROM `eav_entity_store` WHERE (entity_type_id='11') AND (store_id='1')
START TRANSACTION
UPDATE `eav_entity_store` SET `entity_store_id` = '2', `entity_type_id` = '11', `store_id` = '1', `increment_prefix` = '1', `increment_last_id` = '100132298' WHERE (entity_store_id='2')
commit
On s’imagine facilement ce qu’il peut se passer si beaucoup de clients tapent à la porte du site…
Client 1 => SELECT `eav_entity_store`.* FROM `eav_entity_store` WHERE (entity_type_id='11') AND (store_id='1')
Client 2 => SELECT `eav_entity_store`.* FROM `eav_entity_store` WHERE (entity_type_id='11') AND (store_id='1')
Client 1 => START TRANSACTION
Client 1 => UPDATE `eav_entity_store` SET `entity_store_id` = '2', `entity_type_id` = '11', `store_id` = '1', `increment_prefix` = '1', `increment_last_id` = '100132298' WHERE (entity_store_id='2')
Client 1 => commit
Client 2 => START TRANSACTION
Client 2 => UPDATE `eav_entity_store` SET `entity_store_id` = '2', `entity_type_id` = '11', `store_id` = '1', `increment_prefix` = '1', `increment_last_id` = '100132298' WHERE (entity_store_id='2')
Client 2 => commit
et voilà .. 2 commandes avec le même increment id
Ce bug est corrigé dans la version 1.7 en intégrant le SELECT dans la transaction et surtout en lui ajoutant le verrou de lecture “for update”
La méthode en question (Mage_Eav_Model_Mysql4_Entity_Store)
public function loadByEntityStore(Mage_Core_Model_Abstract $object, $entityTypeId, $storeId)
{
$read = $this->_getWriteAdapter();
$select = $read->select()->from($this->getMainTable())
->forUpdate(true)
->where('entity_type_id=?', $entityTypeId)
->where('store_id=?', $storeId);
$data = $read->fetchRow($select);
if (!$data) {
return false;
}
$object->setData($data);
$this->_afterLoad($object);
return true;
}
La solution pour corriger la 1.3 (même si il vaudrait mieux penser à passer à une version supérieure) :
- Nouveau Module
- Surchage de Mage_Eav_Model_Entity_Type->fetchNewIncrementId() pour ajouter le select dans la transaction
- Surcharge de Mage_Eav_Model_Mysql4_Entity_Store->loadByEntityStore() pour ajouter le lock “for update”
Et voilà le rsultat
START TRANSACTION
SELECT `eav_entity_store`.* FROM `eav_entity_store` WHERE (entity_type_id='11') AND (store_id='1') FOR UPDATE
UPDATE `eav_entity_store` SET `entity_store_id` = '2', `entity_type_id` = '11', `store_id` = '1', `increment_prefix` = '1', `increment_last_id` = '100132304' WHERE (entity_store_id='2')
commit