5

We programtically login users automatically to Magento 1.4.0.1; yes, I know it's old, we can't update because of core hacks by another developer.

Each user has the same password as we use our ERP to authenticate through a web service and then send the user's data to this function to log them into Magento to facilitate purchases.

We've been having a lot of SQL deadlock issues with the code shown below as Shortened Code.

This is the exception we've been logging, we can tell it's coming from the code below because that is the ONLY place anywhere in our system where Magento customer info is being updated:

[26-Aug-2015 18:56:12] PHP Fatal error:  Uncaught exception 'Zend_Db_Statement_Exception' with message 
    'SQLSTATE[40001]: Serialization failure: 1213 
        Deadlock found when trying to get lock; try restarting transaction' in /store/lib/Zend/Db/Statement/Pdo.php:235
Stack trace:
#0 /store/lib/Zend/Db/Statement.php(300): Zend_Db_Statement_Pdo->_execute(Array)
#1 /store/lib/Zend/Db/Adapter/Abstract.php(468): Zend_Db_Statement->execute(Array)
#2 /store/lib/Zend/Db/Adapter/Pdo/Abstract.php(238): Zend_Db_Adapter_Abstract->query('UPDATE `magcust...', Array)
#3 /store/lib/Varien/Db/Adapter/Pdo/Mysql.php(333): Zend_Db_Adapter_Pdo_Abstract->query('UPDATE `magcust...', Array)
#4 /store/lib/Zend/Db/Adapter/Abstract.php(604): Varien_Db_Adapter_Pdo_Mysql->query('UPDATE `magcust...', Array)
#5 /store/app/code/core/Mage/Eav/Model/Entity/Abstract.php(1133): Zend_Db_Adapter_Abstract->update('magcustomer_ent...', Array, 'entity_id='9144...')
#6 /store/app in /srv/www/htdocs/store/lib/Zend/Db/Statement/Pdo.php on line 235

I tried using the changes to PDO.php here: http://www.kennguyen.me/magento-deadlock-error/
This caused more trouble than the deadlocks because for some reason our system began having Too many connections errors after I implemented it. I took it back out and need some help determining if I can do something similar.
I thinking that adding the same technique that is used in the above post to our own code should alleviate the deadlocks and this is what I came up with.

SaveCustomer function

function SaveCustomer($customer) {
    $max_tries = 3;
    $tries = 0;
    do {
        $retry = false;
        try {
            $customer->save();
        } catch (PDOException $e) {
            if ($tries < $max_tries && preg_match('/SQLSTATE\[40001\]/', $e->getMessage())) {
                $retry = true;
                sleep(1);
            } else {
                throw new Zend_Db_Statement_Exception($e->getMessage(), (int)$e->getCode(), $e);
            }
            $tries++;
        }
    } while ($retry);
}

So all the $customer->save(); calls will now call SaveCustomer($customer);

Does the above function look like it will fix my problem???

Shortened Code:

function mag_login_or_create($ParticipantInfo, $UserData) {
    //*****************************************************************
    // This is a first time user registering and we pass $UserData
    //*****************************************************************
    if ($ParticipantInfo == false) {
        $ret['error'] = '';
        include_once MAGE_PATH;
        Mage::app();
        $customer = Mage::getModel('customer/customer');
        $customer->setWebsiteId(Mage::app()->getWebsite()->getId());
        $customer->loadByEmail($UserData['PrimaryEmail']);
        if (!$customer->getId()) {
            $customer->setEmail($UserData['PrimaryEmail']);
            $customer->setFirstname($UserData['FirstName']);
            $customer->setLastname($UserData['LastName']);
            $customer->setPassword('password');
            try {
                $customer->save();
                $ret['result'] = 'SUCCESS';
            }
            catch (Exception $e) {
                $ret['result'] = 'FAILURE';
                $ret['error'] .= $e->getMessage();
                return $ret;
            }
        } else {
            $customer->setFirstname($UserData['FirstName']);
            $customer->setLastname($UserData['LastName']);
            try {
                $customer->save();
                $ret['result'] = 'SUCCESS';
            }
            catch (Exception $e) {
                $ret['result'] = 'FAILURE';
                $ret['error'] .= $e->getMessage();
                return $ret;
            }
        }
        //*****************************************************************
        // Now log the customer in using their email address
        //*****************************************************************
        Mage::getSingleton('core/session', array('name'=>'frontend'));
        $session = Mage::getSingleton('customer/session');
        $session->login($UserData['PrimaryEmail'],'********'); // password masked
        if ($session->isLoggedIn()) {
            $session->setCustomerAsLoggedIn($session->getCustomer());
            $ret['result'] = 'SUCCESS';
        } else {
            $ret['result'] = 'FAILURE';
            $ret['error'] .= 'login not established';;
        }      
        return $ret;
    } else {
        //*****************************************************************
        // This is a returning user so $ParticipantInfo is passed
        //*****************************************************************
        $ret['error'] = '';
        include_once MAGE_PATH;
        Mage::app();
        $customer = Mage::getModel('customer/customer');
        $customer->setWebsiteId(Mage::app()->getWebsite()->getId());
        $customer->loadByEmail($ParticipantInfo['PrimaryEmail']);
        if (!$customer->getId()) {
            $customer->setEmail($ParticipantInfo['PrimaryEmail']);
            $customer->setFirstname($ParticipantInfo['FirstName']);
            $customer->setLastname($ParticipantInfo['LastName']);
            $customer->setPassword('********'); // password masked
            try {
                $customer->save();
                $ret['result'] = 'SUCCESS';
            } catch (Exception $e) {
                $ret['result'] = 'FAILURE';
                $ret['error'] .= $e->getMessage();
                return $ret;
            }
        } else {
            $customer->setFirstname($ParticipantInfo['FirstName']);
            $customer->setLastname($ParticipantInfo['LastName']);
            try {
                $customer->save();
                $ret['result'] = 'SUCCESS';
            } catch (Exception $e) {
                $ret['result'] = 'FAILURE';
                $ret['error'] .= $e->getMessage();
                return $ret;
            }
        }

        //*****************************************************************
        // Here, we delete all their addresses so that we can "synchronize" 
        // them with what we have in our ERP.
        //*****************************************************************
        foreach ($customer->getAddresses() as $address) {
            $address->delete();
        }

        //*****************************************************************
        // The code here  creates the new addresses, shortened
        //*****************************************************************
        $regionModel = Mage::getModel('directory/region')->loadByCode($ParticipantInfo['addstate'], $ParticipantInfo['addcountry']);
        $regionId = $regionModel->getId();
        $ParticipantInfoaddress = Mage::getModel("customer/address");
        $ParticipantInfoaddress->setCustomerId($cid);
        $ParticipantInfoaddress->firstname   = $addfname;
        $ParticipantInfoaddress->lastname    = $addlname;
        $ParticipantInfoaddress->prefix      = $addpfix;
        $ParticipantInfoaddress->country_id  = $ParticipantInfo['addcountry'];
        $ParticipantInfoaddress->postcode    = $ParticipantInfo['addzip'];
        $ParticipantInfoaddress->city        = $ParticipantInfo['addcity'];
        $ParticipantInfoaddress->region      = $regionId;
        $ParticipantInfoaddress->street      = array('0'=>$ParticipantInfo['addstreet1'],'1'=>$ParticipantInfo['addstreet2'],'2'=>$ParticipantInfo['addstreet3']);
        $ParticipantInfoaddress->setIsDefaultBilling('1');
        $ParticipantInfoaddress->setIsDefaultShipping('1');
        $ParticipantInfoaddress->setSaveInAddressBook('1');
        $ParticipantInfoaddress->telephone   = (array_key_exists('addphone', $ParticipantInfo)?$ParticipantInfo['addphone']:"---");
        $ParticipantInfoaddress->save();

        //*****************************************************************
        // Now log the customer in using their email address
        //*****************************************************************
        Mage::getSingleton('core/session', array('name'=>'frontend'));
        $session = Mage::getSingleton('customer/session');
        $session->login($ParticipantInfo['PrimaryEmail'],'********'); // password masked
        if ($session->isLoggedIn()) {
            $session->setCustomerAsLoggedIn($session->getCustomer());
            $ret['result'] = 'SUCCESS';
        } else {
            $ret['result'] = 'FAILURE';
            $ret['error'] .= 'login not established';;
        }      
        return $ret;
    }
}
3

Aoe actually wrote an extension for this kind of issue. Aoe_DbRetry tries a query several times when facing deadlocks.

This module is very simple and focus on one task. It replaces the DB adapter with an extended version that will retry queries if the connection is lost, the query cannot obtain a needed lock, or a deadlock occours. These three situations are detected via exception messages. The underlying (parent) code actually wraps at least one of these exceptions up inside another exception, so we check for that and unwrap the exception if needed.

You can download it from Github: https://github.com/AOEpeople/Aoe_DbRetry

| improve this answer | |
  • That is essentially the same thing that the changes to PDO do and as I noted, it caused more problems as it did this for EVERY DB call, which I call more overhead than needed. – MB34 Nov 5 '15 at 16:34
  • @MB34 what where the exact problems you got? It should only put queries in a queue when they fail – Sander Mangel Nov 5 '15 at 18:17
  • As I noted, I attempted to do the PDO change and for some reason our system began having Too many connections errors after I implemented it. – MB34 Nov 5 '15 at 18:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.