PHP事务管理器跨资源

时间:2013-11-05 16:47:39

标签: php symfony transactions

在我们的Symfony2项目中,我们希望确保跨资源的修改是事务性的。例如,像:

namespace ...;
use .../TransactionManager;

class MyService {

    protected $tm;

    public function __construct(TransactionManager $tm)
    {
        $this->tm = $tm;
    }

    /**
     * @ManagedTransaction
     */
    public function doSomethingAcrossResources()
    {
        ...

        // where tm is the transaction manager
        // tm is exposing a Doctrine EntityManager adapter here
        $this->tm->em->persist($entity);

        ...

        // tm is exposing a redis adapter here
        $this->tm->redis->set('foo', 'bar');

        if ($somethingWentWrong) {
            throw new Exception('Something went terribly wrong');
        }
    }
}

所以这里有几点需要注意:

  1. 每个资源都需要一个公开其API的适配器(例如Doctrine适配器,Redis适配器,Memcache适配器,文件适配器等)。
  2. 如果出现问题(抛出异常),则不应将任何内容写入任何托管资源(即回滚所有内容)。
  3. 如果没有出错,所有资源都会按预期更新
  4. doSomethingAcrossResources函数不必担心它对非事务性资源(例如Files和Memcache)所做的更改。这是关键,因为否则,这段代码可能会成为一个混乱的混乱,只能在适当的时候写redis等。
  5. @ManagedTransacton注释将负责其余的工作(提交/回滚/启动所需的事务(基于适配器)等)。
  6. 在最简单的实现中,tm可以简单地管理队列并将所有项目串行出列。如果抛出异常,它就不会出现任何异常。因此,适配器是事务管理器对如何提交队列中每个项目的了解。
  7. 如果在出队期间发生异常,那么事务管理器将查看它的适配器,以了解如何回滚已经出列的项目(可能放在回滚堆栈中)。对于像EntityManager这样需要在内部管理事务以便轻松回滚更改的资源,这可能会变得棘手。但是,redis适配器可能会在更新期间缓存以前的值,或者在ADD期间只是在回滚期间发出DELETE。
  8. 这样的交易经理是否已经存在?有没有更好的方法来实现这些目标?我可能会忽略一些警告吗?

    谢谢!

1 个答案:

答案 0 :(得分:1)

事实证明,我们最终不需要确保资源的原子性。当涉及多个行/表时,我们确实希望与数据库交互成为原子,但我们决定使用事件驱动的体系结构。

如果更新redis在事件监听器内部失败,我们将停止传播,但它不是世界末日 - 允许我们通知用户成功操作(即使副作用不成功)

我们可以根据需要运行后台作业以偶尔更新redis。这使我们能够将核心业务逻辑集中在服务方法中,然后在成功时调度事件,从而允许发生非关键副作用(更新缓存,发送电子邮件,更新弹性搜索等),彼此隔离,在主要业务逻辑之外。