解决Magento ORM竞争条件

时间:2015-03-30 13:49:46

标签: php magento

作为magento专业服务和模块开发人员,我们需要实施Magento订单处理工作流程,其中订单信息从2个独立的并发流程(第三方支付处理通知处理程序和客户到订单分配处理程序)加载,更新和存储。当前的实施工作正常,但该过程容易受到竞争条件问题的影响。

流程A: load()ᴬ - > updateFields1() - >保存()ᴬ

流程B: load()ᴮ - > updateFields2() - >保存()ᴮ

如果在load()after之后但在save()之前调用load()ᴮ,则其中一个进程会覆盖并发进程设置的值。

如果方法updateField1()和updateField2()中更新的字段完全不同,Magento框架是否有可能或某些常规做法来处理竞争条件?

1 个答案:

答案 0 :(得分:1)

基本上有两种方法可以避免这种竞争条件。他们都有权衡。

选项A:进行两次单独的UPDATE查询以更新这些字段,并让数据库服务器为您处理锁定。 Zend的DB Framework已经有了内置的方法来进行这些类型的更新。这里有两个示例函数可以帮助您入门:

function paymentProcessingUpdate($incrementId, $paymentId) {
    $db = Mage::getSingleton('core/resource')->getConnection('core_write');
    $table = 'sales_flat_order';
    $data = array('payment_id' => $paymentId);
    $where = $db->quoteInto('increment_id = ?', $incrementId);
    $db->update($table, $data, $where);
}

function assignCustomerToOrder($incrementId, $customerId) {
    $db = Mage::getSingleton('core/resource')->getConnection('core_write');
    $table = 'sales_flat_order';
    $data = array('customer_id' => $customerId);
    $where = $db->quoteInto('increment_id = ?', $incrementId);
    $db->update($table, $data, $where);
}

这种方法的缺点:

  1. 如果您的字段在sales_flat_order_grid表中使用,则您也必须更新该表。这是Magento的模型通常会为您做的事情。您可以在单个JOIN查询中添加UPDATE来处理此问题。
  2. 绕过Magento模型,这意味着sales_order_save_commit_aftersales_order_save_after等观察者不会被触发。
  3. 优点:

    1. 运行单个更新要比保存整个模型快得多。
    2. 选项B:您可以使用像Mage_Index_Model_Lock这样的锁定机制,这是Magento在刷新索引时所做的工作。

      工作原理:我们假设我们正在处理订单号100002185。在此示例中,过程A和B可以互换。

      1. 进程A启动并检查automation_order_id_100002185是否存在锁定。
      2. 进程A找不到锁,因此它为automation_order_id_100002185设置锁定并开始执行其工作。
      3. 流程B在流程A完成之前启动,并检查automation_order_id_100002185的锁定。
      4. 进程B找到一个锁,因此它会休眠3秒钟。
      5. 流程A仍然有效。进程B在3秒睡眠后再次检查锁定,但进程A尚未释放它,因此它再进行3秒睡眠。
      6. 进程A完成其工作,并释放锁定。
      7. 进程B在3秒睡眠后再次检查锁定。这次没有锁定,因此进程B设置锁定,并开始工作。
      8. 流程B完成工作并释放锁定。
      9. 这种方法的缺点:

        1. 它牺牲了速度。如果每个流程更新的字段不同,不相关且独立,则可以并行完成工作。
        2. 灾难性服务器故障(或重启)可能导致锁永远不会被释放。
        3. 如果永远不会释放锁定,则该过程可能无限期地睡眠,并且其工作可能永远不会完成。这可能导致进程堆叠并最终导致服务器崩溃,从而导致其他进程产生连锁反应。
        4. 如果服务器崩溃,并且您没有某种类型的队列可以依赖,那么您的处理可能永远不会完成。 Magento 2使用RabbitMQ来解决此问题,但您可以使用数据库表和cron脚本实现类似的机制。