引用

ぢみへんプログラミング日誌 ActiveRecord::Base.transaction はネストしたトランザクションに対応しているのか確認した。

細かい説明は抜きにして、以下のirb (rails console -s で起動するコンソールモード)での実行内容を見てみよう。
まずはUserというモデルをsaveメソッド単体で保存してみる。

[root@localhost base]# rails console -s
Loading development environment in sandbox (Rails 4.0.0.beta1)
Any modifications you make will be rolled back on exit
irb(main):001:0> a  = User.new
=> #
irb(main):002:0> a.user_no = "123"
=> "123"
irb(main):003:0> a.user_name = "aiueo"
=> "aiueo"
irb(main):004:0> a.save
  (0.3ms)  BEGIN
  User Exists (1.6ms)  SELECT 1 AS one FROM "users" WHERE "users"."user_no" = '123' LIMIT 1
  SQL (13.1ms)  INSERT INTO "users" ("updated_at", "user_name", "user_no") VALUES ($1, $2, $3, $4) RETURNING "id"  [ ["created_at", Wed, 13 Mar 2013 15:10:12 UTC +00:00], ["updated_at", Wed, 13 Mar 2013 15:10:12 UTC +00:00],["user_name", "aiueo"], ["user_no", "123"]]
   (3.3ms)  COMMIT
=> true

見ての通り、saveメソッドは単体でトランザクションも起動している。


前置き

上記からわかりますが、railsは明示的に指定しなくてもトランザクションを勝手に行ってくれます。

トランザクションの中で例外が起こると自動でロールバックしてくれるわけです。

(上記引用では、saveなので例えDBの書き込みに失敗してもロールバックはしてくれません。save!ならしてくれる。補足:上記サイトは後半で自分でraiseして動作確認を試みている)

railsが自動で行うトランザクションはなんのためか?

ここからは推測ですが、
レコードを1件更新するだけのような場合は失敗してもそもそもDBに更新されたデータがないのでロールバックの必要がありません(そもそもロールバックできる対象がない)。

そこで ではどういうときにロールバックが必要かと考えた場合、
has_manyな関係の処理ではないかと考えました。

Rails4 - 複数の子レコードを作成・更新する. accepts_nested_attributes_for - Qiita

つまり上記サイト(引用)にある

class Member < ActiveRecord::Base
  has_many :posts
  accepts_nested_attributes_for :posts
end

params = { member: {
  name: 'joe', posts_attributes: [
    { title: 'Kari, the awesome Ruby documentation browser!' },
    { title: 'The egalitarian assumption of the modern citizen' },
    { title: '', _destroy: '1' } # this will be ignored
  ]
}}

member = Member.create(params[:member])
member.posts.length # => 2
member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
member.posts.second.title # => 'The egalitarian assumption of the modern citizen'

のようなデータをDBに書き込みにいくときは、Membersテーブルと、Postsテーブルに書き込みにいく処理があるわけでして、どちらか片方のテーブルでエラーがあるとロールバックして欲しいということになります。

この考えから導いた推測は、

  • has_manyな関係の更新のようにrailsが内部で複数テーブルを更新しにいくときは自動トランザクションは!メソッドを使っていた場合、特に意識していなかったがロールバックしてくれてうれしい

  • 1レコードの更新のときは、トランザクションの必要はないのにとりあえず自動でトランザクションが走っている

ということになりました。
しかし、1レコードのときに無駄な処理が走っているという自分なりの結論が何か腑に落ちません。無駄な処理が走るようなものなのでしょうか?