LoginSignup
1
1

More than 3 years have passed since last update.

ActiveRecordのトランザクションのネストに気をつけること

Posted at
  • 確認した環境
    • Ruby 2.5.1
    • Rails 5.2.1
    • MySQL 5.7.27

腕試しに以下のスクリプトを実行した場合、最終行の User.first.name は何が得られるか考えてみてください.

User.first.update!(name: 'Alice')

ActiveRecord::Base.transaction do
  User.first.update!(name: 'Bob')

  ActiveRecord::Base.transaction do
    User.first.update!(name: 'Carol')
    raise ActiveRecord::Rollback
  end
end

User.first.name


正解はここをクリック

User.first.name の結果は Carol になります.
正解しましたか?
正解する人もいると思いますが直感と違うかと思います.









TL;DR

  • 可能ならトランザクションをネストしない設計する
  • トランザクションをネストする設計の際は requires_new: true を使う
    • 付けないと思いのよらないゴミができる可能性がある

検証

分かりやすくするため、SQLをコメントに加えました.

User.first.update!(name: 'Alice')
# BEGIN
# UPDATE `users` SET `name` = 'Alice' WHERE `users`.`id` = 1
# COMMIT

ActiveRecord::Base.transaction do
  # BEGIN

  User.first.update!(name: 'Bob')
  # UPDATE `users` SET `name` = 'Bob' WHERE `users`.`id` = 1

  ActiveRecord::Base.transaction do
    User.first.update!(name: 'Carol')
    # UPDATE `users` SET `name` = 'Carol' WHERE `users`.`id` = 1
    raise ActiveRecord::Rollback
  end
end
# COMMIT

User.first.name # => 'Carol'

requires_new を使った場合

いくつかのRDBにはトランザクションにSAVEPOINTをサポートしているものがあります.
このSAVEPOINTを利用するには requires_new を指定することで有効になり直感に近い挙動をする様になります.

User.first.update!(name: 'Alice')
# BEGIN
# UPDATE `users` SET `name` = 'Alice' WHERE `users`.`id` = 1
# COMMIT

ActiveRecord::Base.transaction do
  # BEGIN

  User.first.update!(name: 'Bob')
  # UPDATE `users` SET `name` = 'Bob' WHERE `users`.`id` = 1

  ActiveRecord::Base.transaction(requires_new: true) do
    # SAVEPOINT active_record_1

    User.first.update!(name: 'Carol')
    # UPDATE `users` SET `name` = 'Carol' WHERE `users`.`id` = 1
    raise ActiveRecord::Rollback
  end
  # ROLLBACK TO SAVEPOINT active_record_1

end
# COMMIT

User.first.name # => 'Carol'
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1