首页 > 解决方案 > 处理事务内的唯一约束异常

问题描述

我有两个 Rails 模型/表,我想将它们作为事务的一部分插入。

例如,这里大致是我的表格:

现在我正在尝试大约相当于:

ActiveRecord::Base.transaction do
  Comment.create!(post: post, user: user, comment_text: '...')

  begin
    Commenters.find_or_create_by!(post: post, user: user)
  rescue PG::UniqueViolation
  end
end

这在 99.9% 的时间都有效,但有时两个并发评论会触发 PG::UniqueViolation。

即使我正在捕获并抑制 PG::UniqueViolation,整个事务也因以下原因而失败:

错误:当前事务被中止,命令被忽略直到事务块结束

我意识到我已经可以通过加入 Post 和 Comment 表来实现这一点,但这是一个简化的示例。

有没有更简单的方法来确保两个插入都作为事务的一部分发生,同时仍然忽略唯一的违规,因为我们可以假设记录已经存在?

标签: ruby-on-railsrubypostgresql

解决方案


事务内部引发的异常有两件事:

  1. 回滚事务,因此事务内部所做的任何更改都不会保留在数据库中。
  2. 异常传播到事务块之外。

因此,您可以将异常处理程序移到transaction调用之外:

begin
  ActiveRecord::Base.transaction do
    Comment.create!(post: post, user: user, comment_text: '...')
    Commenters.find_or_create_by!(post: post, user: user)
  end
rescue PG::UniqueViolation
  retry
end

如果您想要更多安全性,您可以包括一个只重试几次的计数器。


推荐阅读