1

在我们的 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 函数不必担心撤销它对非事务性资源(例如文件和 Memcache)所做的更改。这是关键,因为否则,这段代码可能会变得一团糟,只能在适当的时间写入 redis,等等。
  5. @ManagedTransacton 注释将处理其余部分(提交/回滚/启动所需的事务(基于适配器)等)
  6. 在最简单的实现中,tm 可以简单地管理一个队列并按顺序将所有项目出队。如果抛出异常,它根本不会使任何东西出队。所以适配器是事务管理器关于如何提交队列中每个项目的知识。
  7. 如果在出队期间发生异常,则事务管理器将查看它的适配器以了解如何回滚已经出队的项目(可能放置在回滚堆栈中)。对于像 EntityManager 这样需要在内部管理事务以便轻松回滚更改的资源来说,这可能会变得很棘手。但是,redis 适配器可能会在更新期间缓存以前的值,或者在 ADD 期间简单地在回滚期间发出 DELETE。

像这样的事务管理器是否已经存在?有没有更好的方法来实现这些目标?是否有我可能忽略的警告?

谢谢!

4

1 回答 1

1

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

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

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

于 2013-11-06T04:08:15.630 回答