Database based uniqueness validations for ActiveRecord

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Database based uniqueness validations for ActiveRecord

DjezzzL

You may have noticed that Rails is performing a SELECT 1 FROM query before inserting a new record if there's a uniqueness validation in the model.


This approach has several downsides.

First of all, it's an additional query, and in case there's uniqueness validation on several attributes, several additional queries will be performed.

Second, which is important for applications running at scale, is a race condition, two concurrent create/update operations may end up querying the database for existing records and then add a record to the database that will leave it in an inconsistent state. Of course, this may be resolved with a DB uniqueness constraint, but in this case, you won't get a proper validation message, the request will just error out.


Welcome database_validations gem, which provides compatibility between database constraints and ActiveRecord validations with better performance and consistency.


Skip to the benchmarks to find out that for the case where each hundredth statement is attempting to insert a duplicate is two times faster with this gem.


In majority of cases, validates_db_uniqueness_of is a drop-in replacement for validates_uniqueness_of.

scope, message, if and unless conditionals and index_name are supported on major databases. where is supported on PostgreSQL.


A convenience RSpec matcher is bundled:

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

You must add DB uniqueness constraints on the database level, and if you didn't, the gem will error out with an explanatory message.

The gem is battle tested on an application with almost a hundred uniqueness validations in 50+ models at scale.

Known gotchas:

  • if there's more than one validation failure, only one will be indicated

Related read:


Rails guide on uniqueness validations.

The Perils of Uniqueness Validations blog post.

Use create_or_find_by to avoid race condition in Rails 6.0 blog post. Add #create_or_find_by to lean on unique constraints Rails pull request.

Validation, Database Constraint, or Both? blog post. related Reddit comments.

Do you test simple validations and db structure of models? discussion.

Contributions are welcome. What's on the list:

  • RuboCop cop to detect the uses of valdiates_uniqueness_of and autocorrect them to use DB uniqueness validation

Get over with validates_uniqueness_of and embrace validates_db_uniqueness_of!


P.S. I'm the owner of the gem but the topic was copy-pasted from reddit to spread the information about the gem.

--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/rubyonrails-talk/481ef5b6-8b3a-4ce9-84d6-958c3efb9acb%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.