昔からある機能です。 Rails 0.9.3からの機能あったようです*1。 よくわかっていないので、説明してみます。
Railsガイドの説明
Railsガイドでは次のように説明されています。
Active Record クエリインターフェイス - Railsガイド
楽観的ロックでは、複数のユーザーが同じレコードを同時編集することを許し、データの衝突が最小限であることを仮定しています。この方法では、レコードがオープンされてから変更されたことがあるかどうかをチェックします。そのような変更が行われ、かつ更新が無視された場合、ActiveRecord::StaleObjectError例外が発生します。
いまいちユースケースがピンと来ません。
使い方
Railsガイド中では次のような短いサンプルがあります。
c1 = Customer.find(1) c2 = Customer.find(1) c1.first_name = "Sandra" c1.save c2.first_name = "Michael" c2.save # ActiveRecord::StaleObjectErrorが発生
これだとRuby on Railsアプリケーションの中でどう使えば良いのかよくわかりません。
画面からの編集リクエストとc1
とc2
の関係が想像できません。
用途
GitHubのWikiが楽観的ロックを使っていそうな動作だったような、おぼろげな記憶があります。 確かめてみると、現在は次のようにフラッシュメッセージがPushされます。
記憶の中のイメージでは、Saveボタンを押したときに保存失敗していました。
想像するに、ユースケースは、ある程度長文を保持するモデルで編集内容を上書きされたくない時のようです。 便利にするなら、マージするなり衝突したDiffを表示するなりしたいところです。 そこまで凝った実装をしないで、最低限、別のユーザーからの変更があったことをユーザーに伝えるための機能です。
楽観的ロックを使ったRuby on Railsアプリケーションを作る
なんとなく用途がイメージできたので、実際にアプリケーションを作ってみます。 長文を保持するので、記事モデルを一つ持つRuby on Railsアプリケーションを作ります。
楽観的ロックはlock_version
カラムさえあれば動作します。
scaffold
するときに lock_version
カラムも指定しておきます。
bundle exec rails new -MCAJT --skip-active-job --skip-active-storage . bin/rails g scaffold Article title:string content:text lock_version:integer bin/rails db:prepare
rails
をインストールしていなければbundel init
して出来たGemfileの中のコメントを外してbundle
してください。
触ってみる
次のコマンドでRuby on Railsアプリケーションを起動します。
bin/rails s
http://localhost:3000/articles を開くと記事の一覧画面です。
リンクをたどって記事作成画面を表示します。
scaffold
するときに lock_version
カラムも指定したので、lock_version
が編集出来ます。
適当に記事を作ります。
記事の本文を変更してて保存してみましょう。
lock_version
を変えていないのに、勝手にインクリメントされました。
もう一度記事の本文を変更してみましょう。
今回はlock_version
も一緒に変更し、保存します。
なんか狐につままれたよう気分になりますが、Ruby on Railsアプリケーションの外側でlock_version
が変更されたらActiveRecord::StaleObjectError
が発生します。
以上が楽観的ロックの動作です。
今日はここまで
楽観的ロックの動作はわかりました。 実際のアプリケーションでつかうにはもう少し工夫が必要そうです。
参考
*1:https://github.com/rails/rails/blob/v0.9.3/activerecord/lib/active_record/locking.rbにlock_versionの記述があります。DHHのコミットで追加されました。