首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >具有多个数据库的Rails 3 ActiveRecord嵌套事务

问具有多个数据库的Rails 3 ActiveRecord嵌套事务
EN

Stack Overflow用户
提问于 2012-07-13 16:50:14
回答 2查看 1.7K关注 0票数 1

根据rails文档,涉及多个数据库的嵌套事务应该是有效的:

代码语言:javascript
复制
User.transaction do
  User.create(:username => 'Kotori')
  User.transaction(:requires_new => true) do
    User.create(:username => 'Nemu')
    raise ActiveRecord::Rollback
  end
end

如何传播嵌套事务中的回滚,以便父事务也能回滚?

谢谢

EN

回答 2

Stack Overflow用户

发布于 2012-12-19 02:16:45

首先,您的代码没有使用多个数据库。两者都使用来自客户端模型的连接。

其次,您没有指定DBMS。不同的DB驱动程序具有不同的属性,并不是所有驱动程序都实现嵌套事务。您使用的是哪种数据库?

我只是做了你想做的,并且我使用了两个postgres数据库。只抛出ActiveRecord::Rollback将退出封闭的事务,但不会抛出进一步的异常。我想确保如果嵌套事务失败,则外部事务将回滚。因此,我在内部事务中捕获任何异常,然后从那里引发回滚。

注意:帐户和用户模型使用不同的数据库,尽管它们都运行在Postgres的同一实例中。此外,如果省略了begin rescue子句,那么是的,您的代码将回滚这两个数据库,但在外部事务结束后,将会留下一个展开堆栈的异常。最后,我认为如果提交外部事务时出现问题,那么内部事务将不会回滚。在我的例子中,这不是问题。您可能希望将DB1代码包装在嵌套事务中,并在启动DB2事务之前提交它。

代码语言:javascript
复制
Account.transaction do
  begin
    acc = Account.do_something_to_accounts
    User.transaction do
      User.do_something_to_users
    end
  rescue => e
    raise ActiveRecord::Rollback
  end
end
票数 1
EN

Stack Overflow用户

发布于 2020-02-05 04:31:33

Jonathan所说的是正确的,但他的代码示例并不完全正确。

为了取消两个数据库之间的事务,您需要检测嵌套事务何时将在没有外部事务的情况下回滚。标准异常将不会被捕获,并导致两个事务回滚。但是,ActiveRecord#transaction块将静默地从ActiveRecord::Rollback中拯救和回滚,因此解决方案需要在嵌套事务的每个级别(在本例中只有1个级别)中检测这种情况,并传播它。

一个解决方案可能如下所示:

代码语言:javascript
复制
Account.transaction do
  acc = Account.do_something_to_accounts
  should_rollback = false
  User.transaction do
    begin
      User.do_something_to_users
    rescue ActiveRecord::Rollback
      should_rollback = true
      raise ActiveRecord::Rollback
    end
  end
  raise ActiveRecord::Rollback if should_rollback

end

另一种实现方式:

代码语言:javascript
复制
class NestedTransactionRollback < RuntimeError; end

begin
  Account.transaction do
    acc = Account.do_something_to_accounts
    User.transaction do
      begin
        User.do_something_to_users
      rescue ActiveRecord::Rollback
        raise NestedTransactionRollback
      end
    end
  end
rescue NestedTransactionRollback
  # Rescue silently...
end

编辑:您也可以有点花哨,构建一个通用的解决方案,如下所示:

代码语言:javascript
复制
class DistributedDbRollback < RuntimeError; end

def cross_db_transaction(*ar_classes, nested: false, &block)
  return distributed_yield(&block) if ar_classes.empty?

  ar_classes[0].transaction do
    cross_db_transaction(ar_classes[1..-1], nested: true, &block)
  end
rescue DistributedDbRollback
  raise DistributedDbRollback if nested
  # Return nil if we're in the outermost call
end

def distributed_yield(&block)
  yield
rescue ActiveRecord::Rollback
  raise DistributedDbRollback
end

# Usage:
# cross_db_transaction(User, Account, Invoice) do
#   ...
# end
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11467121

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档