はじめに
Rails 6 に追加された新機能を試す第77段。 今回は、 update_all
編です。
Rails 6 では、 update_all
が楽観的ロックを配慮するようになりました。
Ruby 2.6.3, Rails 6.0.0, Rails 5.2.3 で確認しました。
$ rails --version
Rails 6.0.0
プロジェクトを作る
$ bin/rails new rails_sandbox
$ cd rails_sandbox
今回は、わざと StaleObjectError が発生するような更新処理のメソッドを作って確認します。
User モデルを作る
属性として、 name と age を持つ User モデルを作ります。
楽観的ロックの機能を使うため、 lock_version も追加します。
$ bin/rails g model User name age:integer lock_version:integer
seed データを作成
seed データを作成します。
User.create!(
[
{ name: 'Taro', age: 10 },
{ name: 'Jiro', age: 10 },
{ name: 'Hanako', age: 10 }
]
)
User モデルを修正する
Taro のレコードを検索します。
Taro と Hanako の age を update_all
を使って 11 に更新します。
そのあと、StaleObjectError を発生させるために、検索したTaro の age だけ12 に更新します。
class User < ApplicationRecord
def self.update_age
taro = User.find_by(name: 'Taro')
where(name: %w[Taro Hanako]).update_all(age: 11)
taro.age = 12
taro.save! # => StaleObjectError
end
end
seed データの投入
DB マイグレーションを実行しデータを登録します。
bin/rails db:create db:migrate db:seed
Rails Runner で確認する
Rails Runner で User.update_age
を実行するとStaleObjectError が発生します。
$ bin/rails runner 'User.update_age'
...
27: from /app/models/user.rb:6:in `update_age'
...
/usr/local/bundle/gems/activerecord-6.0.0/lib/active_record/locking/optimistic.rb:95:in `_update_row': Attempted to update a stale object: User. (ActiveRecord::StaleObjectError)`
Rails Console で確認する
rails console
で各レコードを確認すると、 Taro と Hanako の age が 11 に lock_version が 1 になっていることがわかります。 ( update_all
は実行されているが、 save!
メソッドは実行されていない )
irb(main):001:0> User.all.map{|u| [u.name, u.age, u.lock_version]}
User Load (0.5ms) SELECT "users".* FROM "users"
=> [["Jiro", 10, 0], ["Taro", 11, 1], ["Hanako", 11, 1]]
Rails 5 では
Rails 5.2.3 では、エラーは発生せず、最後まで実行されます。
Rails Console で確認すると、Hanako の lock_version は 0 のままで、 Taro の lock_version は、 1 になっています。
update_all
で更新したときは、lock_version は変わらず、 save!
メソッドで更新したときに lock_version が更新されるためです。
irb(main):001:0> User.all.map{|u| [u.name, u.age, u.lock_version]}
User Load (0.4ms) SELECT "users".* FROM "users"
=> [["Jiro", 10, 0], ["Hanako", 11, 0], ["Taro", 12, 1]]
試したソース
試したソースは以下にあります。
https://github.com/suketa/rails_sandbox/tree/try077_update_all