Increment ID identiques sur plusieurs commandes dans Magento 1.3

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

Leave a Reply