回滚嵌套事务中的整个事务

Ari*_*osh 5 activerecord ruby-on-rails ruby-on-rails-5

我想要一个嵌套事务使父事务失败。

假设我有以下模型

class Task < ApplicationRecord
  def change_status(status, performed_by)
    ActiveRecord::Base.transaction do
      update!(status: status)
      task_log.create!(status: status, performed_by: performed_by)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

我总是希望创建updatetask_log创建是一起执行的事务,或者根本不执行。

假设我有一个允许我更新多个任务的控制器

class TaskController < ApplicationController
  def close_tasks
    tasks = Task.where(id: params[:_json])

    ActiveRecord::Base.transaction do
      tasks.find_each do |t|
        t.change_status(:close, current_user)
      end
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

我希望这样,如果任何一个change_status失败,整个请求都会从父级事务中回滚。

然而,这不是 Rails 中的预期行为,请参阅嵌套事务的文档

他们举了两个例子。

User.transaction do
  User.create(username: 'Kotori')
  User.transaction do
    User.create(username: 'Nemu')
    raise ActiveRecord::Rollback
  end
end
Run Code Online (Sandbox Code Playgroud)

这将创建Users“Kotori”和“Nemu”,因为父母永远不会看到加注

然后是下面的例子:

User.transaction do
  User.create(username: 'Kotori')
  User.transaction(requires_new: true) do
    User.create(username: 'Nemu')
    raise ActiveRecord::Rollback
  end
end
Run Code Online (Sandbox Code Playgroud)

这只创建了“Kotori”,因为只有嵌套事务失败了。

那么我怎样才能让 Rails 了解嵌套事务是否失败,从而导致父事务失败。继续上面的示例,我希望“Kotori”和“Nemu”都不会被创建。

eng*_*nky 6

您可以确保交易不可加入

User.transaction(joinable:false) do 
  User.create(username: 'Kotori')
  User.transaction(requires_new: true, joinable: false) do 
    User.create(username: 'Nemu') and raise ActiveRecord::Rollback
  end 
end 
Run Code Online (Sandbox Code Playgroud)

这将导致类似于以下内容的结果:

SQL (12.3ms)  SAVE TRANSACTION active_record_1
SQL (11.7ms)  SAVE TRANSACTION active_record_2
SQL (11.1ms)  ROLLBACK TRANSACTION active_record_2
SQL (13.6ms)  SAVE TRANSACTION active_record_2
SQL (10.7ms)  SAVE TRANSACTION active_record_3
SQL (11.2ms)  ROLLBACK TRANSACTION active_record_3
SQL (11.7ms)  ROLLBACK TRANSACTION active_record_2 
Run Code Online (Sandbox Code Playgroud)

您当前的示例结果如下

SQL (12.3ms)  SAVE TRANSACTION active_record_1
SQL (13.9ms)  SAVE TRANSACTION active_record_2
SQL (28.8ms)  ROLLBACK TRANSACTION active_record_2
Run Code Online (Sandbox Code Playgroud)

requires_new: true创建“新”事务(通常通过保存点)时,回滚仅适用于该事务。当该事务回滚时,它只是丢弃该事务并利用保存点。

通过使用requires_new: true, joinable: falserails,将为这些新事务创建保存点,以模拟真正的嵌套事务的概念,并且当调用回滚时,它将回滚所有事务。

你可以这样想:

  • requires_new: true阻止此事务加入其父事务
  • joinable: false意味着父交易不能被子交易加入

当使用两者时,您可以确保任何事务都不会被丢弃,并且任何地方的 ROLLBACK 都会导致任何地方的 ROLLBACK。

  • 如果您查看源代码,您可以看到,如果当前事务不是“ joinable` 语句将在新事务中执行。 (2认同)