真の一意性検証

Ruby On Railsで作業したことのあるすべてのハッカーORM ActiveRecordに精通しています。 提案されている検証の1つ、つまり一意性の検証、およびdatabase_validations gemがデータベースの一貫性を保存する理由について説明します。



メールフィールドに一意性のあるユーザーモデルがあるとしましょう。



class User < ApplicationRecord validates :email, uniqueness: true end
      
      





この検証が次のリクエストを実行することを既に知っているかもしれません



 SELECT 1 FROM users WHERE email = $1
      
      





データベースにレコードを保存しようとするたびに。



このアプローチにはいくつかの欠点があります。



まず 、追加のリクエストの実行、およびモデル内で複数の一意性検証が初期化されている場合、リクエストはそれぞれに対して実行されます。 これは効率的ではなく、これらのクエリをすばやく実行する場合はインデックスも必要です。



第二に 、このソリューションは、データの競合の可能性があるため、一意性を保証しません。 いくつかの競合事業は、特定のレコードが存在しないことを同時に学習し、その結果、同じデータを保持することができます。



もちろん、データの競合を伴うまれなケースは、データベースレベルで一意性制約を追加することで解決できます。 ただし、この場合、検証エラーは発生せず、データベースへのクエリは単純に失敗し、トランザクション全体がロールバックされます。



この状況では、gem database_validationsが役立ちます。これにより、データベースの制限と検証の間の互換性が提供されます。



gemの主な意味は、次のコードに示されています。



 def save(options = {}) ActiveRecord::Base.connection.transaction(requires_new: true) { super } rescue ActiveRecord::RecordNotUnique => e Helpers.handle_unique_error!(self, e) false end
      
      







したがって、他のすべての検証に合格した場合、トランザクションが失敗してロールバックした場合、データを保存しようとします。エラーを解析し、オブジェクトのerrors



正しい値を割り当てます。



ドキュメントベンチマークを確認した後、このgemにより、レコードをデータベースに少なくとも2回保存するプロセスが高速化されると結論付けることができます。



PostgreSQLSQLiteMySQL 、およびvalidates_uniqueness_of



との下位互換性などのデータベースのサポートのおかげで、 validates_uniqueness_of



に置き換えるプロセスには数分かかります。



RSpecの便利なマッチャーもすぐに使用できます。



 specify do expect(described_class) .to validate_db_uniqueness_of(:field) .with_message('duplicate') .with_where('(some_field IS NULL)') .scoped_to(:another_field) .with_index(:unique_index) end
      
      





新しい検証に切り替えるときは、データベースの一意性に制限を設ける必要がありますが、まだ存在しない場合、gemはアプリケーションの起動時にこれを示します。



この宝石は、50以上のモデル間での一意性について100以上の検証を行ったアプリケーションでテストされました。



gemを使用して意見を共有してください。 さらなる開発への貢献は大歓迎です!



All Articles