Rails4のtransactionでハマった <- これは私の間違いでした
申し訳ありません この記事完全に自分の間違いでした。
まず、自分のrailsアプリでは、、もろもろの深淵な理由で、モデルを作るときにActiveRecord::Baseを直接継承せずに、全てのモデルに共通で使うモデルを再定義しています。
なぜこんな事をしているかというと、これまた深淵な理由で、connection poolingができないからです。
まあ、そのモデルの基底クラスでゴニョゴニョしているせいで、ActiveRecord::Base使ってDBに繋ぎに行く場合と、モデルからDBに繋ぎに行く場合でコネクションが別々になっていました。
本当に申し訳ないです。。。。
つまりこの問題は、私のrailsアプリのみで起こる問題でした。。。。。
普通にrailsアプリを作っていれば、問題なくロールバックできます。(ActiveRecord::Baseを直接継承して試してみたら問題なくロールバックできました。)
なので↓ の内容は間違いです。
rails4.1でtransactionの処理を下記のように書いてみた。
ActiveRecord::Base.transaction do hoge.destory end
こうすると、transaction内でhoge.destory
の直後に例外起きても、Rollbackしなかった。。。。
Hoge.transaction do hoge.destory end
こうすればOK。
ログみてると、ActiveRecord::Base.transaction
のコネクションとhoge.destory
のコネクションが別物のような感じでした。
簡単に調べてみた。
% bundle exec rails console mysql> show processlist; +-----+------+-----------+--------------------+---------+------+-------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +-----+------+-----------+--------------------+---------+------+-------+------------------+ | 199 | root | localhost | NULL | Query | 0 | init | show processlist | | 208 | root | localhost | server_development | Sleep | 3 | | NULL | +-----+------+-----------+--------------------+---------+------+-------+------------------+ 2 rows in set (0.00 sec) この時一本コネクション張っている [1] pry(main)> ActiveRecord::Base.connection mysql> show processlist; +-----+------+-----------+--------------------+---------+------+-------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +-----+------+-----------+--------------------+---------+------+-------+------------------+ | 199 | root | localhost | NULL | Query | 0 | init | show processlist | | 208 | root | localhost | server_development | Sleep | 3 | | NULL | +-----+------+-----------+--------------------+---------+------+-------+------------------+ 2 rows in set (0.00 sec) 一本のまま [2] pry(main)> Hoge.connection mysql> show processlist; +-----+------+-----------+--------------------+---------+------+-------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +-----+------+-----------+--------------------+---------+------+-------+------------------+ | 199 | root | localhost | NULL | Query | 0 | init | show processlist | | 208 | root | localhost | server_development | Sleep | 86 | | NULL | | 209 | root | localhost | server_development | Sleep | 24 | | NULL | +-----+------+-----------+--------------------+---------+------+-------+------------------+ 3 rows in set (0.00 sec) んんん増えた。。。。 [3] pry(main)> Foo.connection mysql> show processlist; +-----+------+-----------+--------------------+---------+------+-------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +-----+------+-----------+--------------------+---------+------+-------+------------------+ | 199 | root | localhost | NULL | Query | 0 | init | show processlist | | 208 | root | localhost | server_development | Sleep | 188 | | NULL | | 209 | root | localhost | server_development | Sleep | 126 | | NULL | +-----+------+-----------+--------------------+---------+------+-------+------------------+ 3 rows in set (0.00 sec) コレ以上は増えない
ん、増える。。。。
ActiveRecord::Baseとそれを継承したモデルだとコネクション共有してないのかな。
rails3.2.17だとコネクションは増えないので、rails4での挙動っぽい。
この部分が影響してるのかな、(まだ確認していない)
- Ruby on Rails 4.1 Release Notes
- Removed deprecated ActiveRecord::Base#connection method. Make sure to access it via the class.
コミットはここらへんかな
- Access an instance's connection via its class, rather than via #connection
- remove deprecated
ActiveRecord::Base#connection
method.
ちょっと詳細調べていきます。
追記
- 2014/04/19
rails 4.0.4でも同じ挙動だった。