QA@IT

Rails でDBの重複エラーが発生した場合の対処について

6060 PV

Rails の ActiveRecord のモデルで属性の一意性を保証しようと validates_uniqueness_of を設定しても、内部ではその値が既存かどうかを select でチェックしてから insert/update するだけなので、複数のクライアントから同時に更新処理が行われた場合は重複してしまう可能性があります。一意性を保証するためにはDB側でカラムをユニークと宣言しておく必要があります。

ところが DB側で重複エラーが発生した場合 ActiveRecord は uniqueness バリデーションのチェックに引っかかったとはみなしてくれず ActiveRecord::RecordNotUnique 例外でアプリがエラーになってしまいます。

世の中の Rails 製アプリはこれにどのように対処しているのでしょう。各コントローラーで ActiveRecord::RecordNotUnique を rescue してそれなりの処理をしているのでしょうか、それとも稀にしか発生しないからということで特に対処していないとかでしょうか。

何か良い方法があれば教えていただきたいです。

回答

おっしゃるとおり、validates_uniqueness_ofは厳密には役に立たないものなので、あくまで「ユーザフレンドリーなエラーメッセージを表示するため(そのメールアドレスはすでに使われています、など)」に使うものだという認識です。

で、おっしゃるとおり ActiveRecord::RecordNotUnique については、submit buttonをちゃんとdisable_withしてあれば、理論上は起きえないので、むしろ積極的に例外を吐かせて、発生したらどのような状況下で発生したのか知るためにAirbrakeなどで通知が欲しいところです。

では逆に、レースが頻繁に発生するような激しい更新用途ではどうか?というと、毎回validates_uniqueness_ofするコストが無駄なので、そこはあえてvalidatorを設置せずにActiveRecord::RecordNotUniqueだけで処理したりします。

なお余談ですが、Railsのバージョンによっては文字列型のフィールドにおいて validates_uniqueness_of がインデックスを使わない(!)クエリーを投げることがあります。このあたりは何度も変更があって詳しく説明するのが大変なので、:case_sensitiveオプションをつけたり外したりして、きちんと挙動を確認されることをおすすめします。

編集 履歴 (4)
ウォッチ

この質問への回答やコメントをメールでお知らせします。