Rails 3.2.13 の Associations でデッドロックが起こるかもしれない問題
データベースに InnoDB の MySQL を用いた Rails アプリケーションで、次のような二つのクラスを用意します。
class Parent < ActiveRecord::Base attr_accessible :name has_many :children, :dependent => :destroy end class Child < ActiveRecord::Base attr_accessible :name belongs_to :parent end
この状態で rails console からトランザクションを使ったデータ登録を試してみます。
Parent.transaction do parent = Parent.new parent.name = 'parent' parent.save child = Child.new child.parent = parent child.name = 'child' child.save end
SQL のログを見ると次のように実行されています。
(0.2ms) BEGIN SQL (0.4ms) INSERT INTO `parents` (`created_at`, `name`, `updated_at`) VALUES ('2013-05-17 06:42:43', 'parent', '2013-05-17 06:42:43') SQL (0.3ms) INSERT INTO `children` (`created_at`, `name`, `parent_id`, `updated_at`) VALUES ('2013-05-17 06:42:43', 'child', 2, '2013-05-17 06:42:43') (0.6ms) COMMIT
次に、今作ったデータを :dependent => :destroy を利用して一気に消してみます。
parent = Parent.first
parent.destroy
こちらはこんな感じ。
(0.2ms) BEGIN Child Load (0.5ms) SELECT `children`.* FROM `children` WHERE `children`.`parent_id` = 2 SQL (0.3ms) DELETE FROM `children` WHERE `children`.`id` = 2 SQL (0.2ms) DELETE FROM `parents` WHERE `parents`.`id` = 2 (0.8ms) COMMIT
この段階で嫌な予感がした方がいるかもしれませんが、2つのコンソールから MySQL の対話型インターフェースにアクセスし、次のような順番で先ほどの SQL を実行していきます。
BEGIN; -- transaction 1 BEGIN; -- transaction 2 INSERT INTO `parents` (`created_at`, `name`, `updated_at`) VALUES ('2013-05-17 06:42:43', 'parent', '2013-05-17 06:42:43'); -- transaction 1 DELETE FROM `children` WHERE `children`.`id` = 2; -- transaction 2 DELETE FROM `parents` WHERE `parents`.`id` = 2 -- transaction 2 INSERT INTO `children` (`created_at`, `name`, `parent_id`, `updated_at`) VALUES ('2013-05-17 06:42:43', 'child', 2, '2013-05-17 06:42:43'); -- transaction 1
COMMIT は書いていませんが、おそらく最後の INSERT を実行した段階で、そのターミナルに返答が無くなるはずです。それぞれの機能自体はおかしい事をしているわけではありませんが、このような原因でバグが発生する事もあるかもしれないので、備忘録的にメモしておきます。