Timeout.timeoutでrollbackされない件 (Rails)

August 27, 2022

確認環境

$ bundle exec ruby --version
ruby 2.7.5p203 (2021-11-24 revision f69aeb8314) [x86_64-darwin19]
$ bundle exec rails --version
Rails 6.0.4.6

前提

このようなモデル/データがあるとします。

$ rails c
Loading development environment (Rails 6.0.4.6)
irb(main):001:0> u = SampleUser.last
   (0.8ms)  SELECT sqlite_version(*)
  SampleUser Load (0.1ms)  SELECT "sample_users".* FROM "sample_users" ORDER BY "sample_users"."id" DESC LIMIT ?  [["LIMIT", 1]]
=> #<SampleUser id: 1, name: "hoge", created_at: "2022-08-24 15:34:58", updated_at: "2022-08-24 15:34:58">

Timeout の中でトランザクション

irb(main):002:1* Timeout.timeout(2) do
irb(main):003:2*   ActiveRecord::Base.transaction do
irb(main):004:2*     u = SampleUser.find(1)
irb(main):005:2*     u.name = 'new name'
irb(main):006:2*     u.save
irb(main):007:2*
irb(main):008:2*     sleep(5)
irb(main):009:1*   end
irb(main):010:0> end
   (0.1ms)  begin transaction
  SampleUser Load (0.2ms)  SELECT "sample_users".* FROM "sample_users" WHERE "sample_users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  SampleUser Update (1.4ms)  UPDATE "sample_users" SET "name" = ?, "updated_at" = ? WHERE "sample_users"."id" = ?  [["name", "new name"], ["updated_at", "2022-08-27 06:18:46.114153"], ["id", 1]]
   (2.3ms)  commit transaction
Traceback (most recent call last):
        4: from (irb):2
        3: from (irb):3:in `block in irb_binding'
        2: from (irb):8:in `block (2 levels) in irb_binding'
        1: from (irb):8:in `sleep'
Timeout::Error (execution expired)

トランザクションが commit されており、rollback されていません。

トランザクションの中でTimeout

irb(main):013:1* ActiveRecord::Base.transaction do
irb(main):014:2*   Timeout.timeout(2) do
irb(main):015:2*     u = SampleUser.find(1)
irb(main):016:2*     u.name = 'new name 222'
irb(main):017:2*     u.save
irb(main):018:2*
irb(main):019:2*     sleep(5)
irb(main):020:1*   end
irb(main):021:0> end
   (0.1ms)  begin transaction
  SampleUser Load (0.1ms)  SELECT "sample_users".* FROM "sample_users" WHERE "sample_users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  SampleUser Update (0.3ms)  UPDATE "sample_users" SET "name" = ?, "updated_at" = ? WHERE "sample_users"."id" = ?  [["name", "new name 222"], ["updated_at", "2022-08-27 06:20:45.414843"], ["id", 1]]
   (0.4ms)  rollback transaction
Traceback (most recent call last):
        4: from (irb):12
        3: from (irb):13:in `block in irb_binding'
        2: from (irb):18:in `block (2 levels) in irb_binding'
        1: from (irb):18:in `sleep'
Timeout::Error (execution expired)

この場合は、rollbackされています。

Timeout の中でトランザクション (Timeoutの第2引数に例外指定)

Timeout.timeoutのTimeoutの第2引数に例外を指定すると、rollbackされます。

内部の動きについては、こちらの記事に書いてあるようです。(私は、まだ理解していません)

irb(main):025:0>
irb(main):026:1* Timeout.timeout(2, Timeout::Error) do
irb(main):027:2*   ActiveRecord::Base.transaction do
irb(main):028:2*     u = SampleUser.find(1)
irb(main):029:2*     u.name = 'new name 333'
irb(main):030:2*     u.save
irb(main):031:2*
irb(main):032:2*     sleep(5)
irb(main):033:1*   end
irb(main):034:0> end
   (0.1ms)  begin transaction
  SampleUser Load (0.1ms)  SELECT "sample_users".* FROM "sample_users" WHERE "sample_users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  SampleUser Update (0.5ms)  UPDATE "sample_users" SET "name" = ?, "updated_at" = ? WHERE "sample_users"."id" = ?  [["name", "new name 333"], ["updated_at", "2022-08-27 06:22:58.254487"], ["id", 1]]

   (0.5ms)  rollback transaction

参考


SHARE

Profile picture

Written by tamesuu