忍者ブログ

ぢみへんプログラミング日誌

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

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メソッドは単体でトランザクションも起動している。

では今度はトランザクションをネストさせてsaveを使ってみる。

irb(main):061:0> ActiveRecord::Base.transaction do
irb(main):062:1> a = User.new
irb(main):063:1> a.user_no = "234"
irb(main):064:1> a.user_name = "aiueo"
irb(main):065:1> a.user_category_id = 1
irb(main):066:1> a.user_qualification_id = 1
irb(main):067:1> a.card_registered_date = "20130313"
irb(main):068:1> a.library_id = 1
irb(main):069:1> a.save
irb(main):070:1> end
(0.3ms) BEGIN
User Exists (1.2ms) SELECT 1 AS one FROM "users" WHERE "users"."user_no" = '234' LIMIT 1
SQL (2.1ms) INSERT INTO "users" ("created_at", "updated_at", "user_name", "user_no") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Wed, 13 Mar 2013 15:41:55 UTC +00:00], ["updated_at", Wed, 13 Mar 2013 15:41:55 UTC +00:00], ["user_name", "aiueo"], ["user_no", "234"]]
(3.0ms) COMMIT
=> true

saveメソッドの内部にもトランザクション処理が起きて、いわゆるトランザクションのネストが発生するかと思ったが、SQLレベルでBeginが2回生成されていないこと、警告(筆者の環境ではDBがPostgreSQLなのでBEGINが2回続くと警告が出る)が出ないところから見ると、saveメソッド内でトランザクションを開始してはいないようだ。

では明示的にトランザクションをネストさせたらどうなるだろうか

irb(main):072:0> ActiveRecord::Base.transaction do
irb(main):073:1* a = User.new
irb(main):074:1> a.user_no = "345"
irb(main):075:1> a.user_name = "aiueo"
irb(main):080:1> a.save
irb(main):081:1>
irb(main):082:1* ActiveRecord::Base.transaction do
irb(main):083:2* b = User.new
irb(main):084:2> b.user_no = "456"
irb(main):085:2> b.user_name = "aiueo"
irb(main):090:2> b.save
irb(main):091:2> end
irb(main):092:1> end
(0.3ms) BEGIN
User Exists (0.9ms) SELECT 1 AS one FROM "users" WHERE "users"."user_no" = '345' LIMIT 1
SQL (2.9ms) INSERT INTO "users" ("created_at", "updated_at", "user_name", "user_no") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Wed, 13 Mar 2013 15:53:26 UTC +00:00],["updated_at", Wed, 13 Mar 2013 15:53:26 UTC +00:00], ["user_name", "aiueo"], ["user_no", "345"]]
User Exists (0.8ms) SELECT 1 AS one FROM "users" WHERE "users"."user_no" = '456' LIMIT 1
SQL (1.3ms) INSERT INTO "users" ("created_at", "updated_at", "user_name", "user_no") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Wed, 13 Mar 2013 15:53:26 UTC +00:00], ["updated_at", Wed, 13 Mar 2013 15:53:26 UTC +00:00],["user_name", "aiueo"], ["user_no", "456"]]
(3.5ms) COMMIT
=> true

ご覧のとおり、トランザクションはネストしない。

このことはメソッドを介しても全く同様。

irb(main):094:0> def test
irb(main):095:1> ActiveRecord::Base.transaction do
irb(main):096:2* a = User.new
irb(main):097:2> a.user_no = "789"
irb(main):098:2> a.user_name = "aiueo"
irb(main):103:2> a.save
irb(main):104:2> end
irb(main):105:1> end
=> nil
irb(main):106:0> ActiveRecord::Base.transaction do
irb(main):107:1* a = User.new
irb(main):108:1> a.user_no = "890"
irb(main):109:1> a.user_name = "aiueo"
irb(main):114:1> a.save
irb(main):115:1>
irb(main):116:1* test
irb(main):117:1> end
(0.4ms) BEGIN
User Exists (0.5ms) SELECT 1 AS one FROM "users" WHERE "users"."user_no" = '890' LIMIT 1
SQL (0.5ms) INSERT INTO "users" "created_at","updated_at", "user_name", "user_no") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Wed, 13 Mar 2013 16:02:05 UTC +00:00], ["updated_at", Wed, 13 Mar 2013 16:02:05 UTC +00:00],["user_name", "aiueo"], ["user_no", "890"]]
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."user_no" = '789' LIMIT 1
SQL (0.3ms) INSERT INTO "users" "created_at", "updated_at","user_name", "user_no") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Wed, 13 Mar 2013 16:02:05 UTC +00:00], ["updated_at", Wed, 13 Mar 2013 16:02:05 UTC +00:00],["user_name", "aiueo"], ["user_no", "789"]]
(1.1ms) COMMIT
=> true

このように、ActiveRecord::Base.transactionを明示して行うトランザクション処理ではネストが発生しない。

そのためロールバックも非常に明確となる。
ActiveRecord::Base.transaction のブロック中に、ActiveRecord::Base.transactionブロックをネストしても見た目とは反対に実際にはトランザクションがネストしないので、一番ネストの深いブロックで例外が発生しても、全体としてはBEGIN => Rollback の制御が1回起きるだけだ。
以下が論より証拠である。

irb(main):001:0> ActiveRecord::Base.transaction do
irb(main):002:1* a = User.new
irb(main):003:1> a.user_no = "901"
irb(main):004:1> a.user_name = "aiueo"
irb(main):009:1> a.save
irb(main):010:1>
irb(main):011:1* ActiveRecord::Base.transaction do
irb(main):012:2* b = User.new
irb(main):013:2> b.user_no = "012"
irb(main):014:2> b.user_name = "aiueo"
irb(main):019:2> b.save
irb(main):020:2> raise "Exception occured."
irb(main):021:2> end
irb(main):022:1> end
(0.1ms) BEGIN
User Exists (0.8ms) SELECT 1 AS one FROM "users" WHERE "users"."user_no" = '901' LIMIT 1
SQL (5.5ms) INSERT INTO "users" ("created_at", "updated_at","user_name", "user_no") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Wed, 13 Mar 2013 16:20:52 UTC +00:00], ["updated_at", Wed, 13 Mar 2013 16:20:52 UTC +00:00],["user_name", "aiueo"], ["user_no", "901"]]
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."user_no" = '012' LIMIT 1
SQL (0.3ms) INSERT INTO "users" ("created_at", "updated_at", "user_name", "user_no") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Wed, 13 Mar 2013 16:20:52 UTC +00:00],["updated_at", Wed, 13 Mar 2013 16:20:52 UTC +00:00],["user_name", "aiueo"], ["user_no", "012"]]
(0.8ms) ROLLBACK
RuntimeError: Exception occured.

上記通りにActiveRecord::Base.transactionをに使う限りにおいては、コード上でトランザクションブロックをいくらネストさせても、どこかで例外が発生した時点で全ての処理がロールバックされ、セーブポイント等は考慮されない。
セーブポイントが重要という場合は困るだろうが、トランザクションの一意性を考えるとロールバックと同時にトランザクション内全てのレコード処理が自動で無効になってくれたほうが親切だと言えるだろう。これで心置きなくトランザクションを安心して使えるというわけだ。
PR

COMMENT

NAME
TITLE
MAIL (非公開)
URL
EMOJI
Vodafone絵文字 i-mode絵文字 Ezweb絵文字
COMMENT
PASS (コメント編集に必須です)
SECRET
管理人のみ閲覧できます
 
  

カレンダー

03 2024/04 05
S M T W T F S
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

フリーエリア

最新CM

バーコード

ブログ内検索

Copyright ©  -- ぢみへんプログラミング日誌 --  All Rights Reserved

Design by CriCri / Material by petit sozai emi / powered by NINJA TOOLS / 忍者ブログ / [PR]