先提交事务还是先触发事件?

发布于 2022-09-11 22:01:23 字数 941 浏览 28 评论 0

    /**
     * 创建一个用户和用户的关联数据
     * @param array $userData
     * @param array $userFinanceData
     * @return UserModel
     * @throws \Exception
     */
    public function register($userData, $userFinanceData)
    {
        $db = UserModel::query()->getConnection();
        try{
            $db->beginTransaction();

            $user = UserModel::create($userData);
            $userFinanceData['user_id'] = $user->id;
            FinanceAccountModel::create($userFinanceData);
            // 事件触发放在这里 1 ?
            $db->commit();
        }catch (\Exception $exception){
            $db->rollBack();
            throw $exception;
        }
        // 事件触发放在这里 2 ?
        event(new UserCreatedEvent($user));
        return $user;
    }

如果放在[1]处,那么如果数据库事务提交失败,事件却发出去了。导致事件消费者取不到该用户的数据。
如果放在[2]处,那么如果事件触发失败(例如redis连接错误),用户却创建成功了,但是创建用户后需要做的一些事情没有做,导致用户数据不完整。

怎么做才好?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

难如初 2022-09-18 22:01:23

这种问题,你没法避免的,分布式事务的问题。
有一种可以参考下
你在数据库表里面建立一个事件表,就是要发的事件,随事务一起提交。提交完后,你再去发了,发完记得在数据库的记录上做一个标记了,一个状态是新建未发送,一个是已发送。另外,还得有一个线程池去扫那个表,去发那些发失败的。
接受事件的地方,还得做幂等。
如果要更可靠的话,事件表的标记在加一个已处理,
反正要就是比较麻烦了,要考虑的很多

快乐很简单 2022-09-18 22:01:23

给一个方案,用户数据增加一个 step 字段,表示生成过程的整个步骤,每完成一步改一下,然后定时查看数据库里没有完成的数据,然后继续执行,可以保证最终一致性。
这样设计的一个要求是,事件触发的处理程序是幂等的,同一个事件,只会触发一次或者说触发多次不会有副作用

// 生成用户
$db->beginTransaction();
$user = UserModel::create($userData);
$user->step = 1;
$db->commit();

// 触发事件
$db->beginTransaction();
event(new UserCreatedEvent($user));
$user->step = 2;
$db->commit();

定时任务

// 获取生成 3 分钟后还没发通知的用户数据
$sql = 'select * form user where status=1 and addtime < data_sub(now(), INTERVAL 3 MINUTE)';
...
// 重新发送
event(new UserCreatedEvent($user));
$user->step = 2;
$db->commit();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文