検証結果

lockしたレコードはtransactionがcommitされるまでロックされ続ける。

実行結果

| コンソール1 | コンソール2 | |—————————–|————————–| | 検索前: 03:01:03.185, 100 | ↓ | | 検索後: 03:01:03.188, 100 | ↓ | | ロック前: 03:01:03.189, 100 | ↓ | | ロック後: 03:01:03.193, 100 | ↓ | | スリープ前: 03:01:03.194, 100 | ↓ | | ↓ | 検索前: 03:01:04.203, 100 | | ↓ | 検索後: 03:01:04.208, 100 | | ↓ | ロック前: 03:01:04.210, 100 | | スリープ後: 03:01:08.201, 100 | ↓ | | 更新前: 03:01:08.207, 100 | ↓ | | 更新後: 03:01:08.216, 0 | ↓ | | スリープ前: 03:01:08.218, 0 | ↓ | | スリープ後: 03:01:13.197, 0 | ↓ | | ロールバック前: 03:01:13.203, 0 | ↓ | | ↓ | ロック後: 03:01:13.210, 100 | | ↓ | 更新前: 03:01:13.213, 100 | | ロールバック後: 03:01:13.219, 100 | ↓ | | | 更新後: 03:01:13.220, 50 |

検証に用いたコード

コンソール1で実行(first_execution)

def now; Time.now.strftime("%H:%M:%S.%L"); end
def tagging(action, timeline)
  timeline << "#{action}前: #{now}, #{point}"
  value = yield
  timeline << "#{action}後: #{now}, #{point}"

  return value
end
def point
  UserPoint.find(1).point
end

def first_execution
  timeline = []

  rup = UserPoint.find(1)
  rup.assign_attributes(point: 100)
  rup.save!

  begin
    ApplicationRecord.transaction do
      user_point = tagging("検索", timeline, &-> () { UserPoint.find(1) })

      tagging("ロック", timeline, &-> () { user_point.lock! })

      tagging("スリープ", timeline, &-> () { sleep 5 })

      user_point.point = user_point.point - 100
      tagging("更新", timeline, &-> () { user_point.save! })

      tagging("スリープ", timeline, &-> () { sleep 5 })

      tagging("ロールバック", timeline, &-> () { raise ActiveRecord::Rollback })
    end
  ensure
    timeline << "ロールバック後: #{now}, #{point}"
    puts timeline
  end
end

コンソール2で実行(second_execution)

def now; Time.now.strftime("%H:%M:%S.%L"); end
def tagging(action, timeline)
  timeline << "#{action}前: #{now}, #{point}"
  value = yield
  timeline << "#{action}後: #{now}, #{point}"

  return value
end
def point
  UserPoint.find(1).point
end

def second_execution
  timeline = []

  begin
    ApplicationRecord.transaction do
      user_point = tagging("検索", timeline, &-> () { UserPoint.find(1) })

      tagging("ロック", timeline, &-> () { user_point.lock! })

      user_point.point = user_point.point - 50
      tagging("更新", timeline, &-> () { user_point.save! })
    end
  ensure
    puts timeline
  end
end