0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails】varidatesだけだとなぜデータベースの一意性が担保できないのか?

Posted at

はじめに

こんにちは。アメリカで独学でエンジニアを目指している者です。
現在 Railsチュートリアルを使用して勉強していますが、RailsのActiveRecordにおけるvalidatesメソッドは、アプリケーションレベルでのデータ検証を行うために非常に有用であることを学びました
しかし、特にuniquenessバリデーションにおいては、単にvalidatesだけではデータベース上での一意性を完全に保証することができません。
本記事では、その理由と対処法、そしてなぜインデックス(特に一意性インデックス)を用いることで一意性が担保できるのかについて詳しく解説します。

validatesだけではダメな理由

validates :email, uniqueness: { case_sensitive: false }のようなバリデーションは、レコードを保存する前に現在のデータベースの状態に対してクエリを発行し、同じ値が既に存在しないかをチェックします
上記のバリデーションは、リクエストごとに生成されたインスタンス上で行われるため、同時に発生する複数のリクエスト間で情報を共有することはできません。
つまり、リクエスト1とリクエスト2がほぼ同時に実行されると、どちらも検証時点では重複がないと判断され、結果として両方のリクエストが成功してしまう可能性があります

対処法

  • 一意性インデックスの追加:
    現在私が使用しているRailsチュートリアルでは、対象カラムに対して一意性インデックスを設定する手法を採用しています。
    これによって、データベースの保存時にデータベース自体が重複を検出しエラーを返すという仕組みになっています。
    たとえば、以下のようなマイグレーションファイルを作成します。
class AddIndexToUsersEmail < ActiveRecord::Migration[6.0]
  def change
    add_index :users, :email, unique: true
  end
end

ちなみに一意性インデックスが設定されている場合は以下の処理が行われます

  • 1つのリクエストがレコードを挿入しようとすると、データベースはそのトランザクションが完了するまで新しいデータをロックします。
  • もし別のリクエストが同じ email を挿入しようとすると、インデックスによるチェックが働き、重複エラー (ActiveRecord::RecordNotUnique) を発生させます。

まとめ

  • validatesのみでは不十分:
    ActiveRecordvalidates :uniquenessはアプリケーションレベルでの検証であり、複数リクエスト間の競合状態に対して脆弱です。
    そのため、単独ではデータベース上の一意性を完全には保証できません。

  • 対処法:
    データベースレベルでの一意性制約(一意性インデックス)の追加があげられます

  • インデックスによる保証:
    一意性インデックスはデータベース内部で重複チェックを行い、トランザクションやロック機構を活用するため、並行リクエストがあっても確実に一意性を担保できます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?