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
unless conditionals and
index_name are supported on major databases.
where is supported on PostgreSQL.
A convenience RSpec matcher is bundled:
.with_where(’(some_field IS NULL)’)
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.
- if there’s more than one validation failure, only one will be indicated
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.
Contributions are welcome. What’s on the list:
- RuboCop cop to detect the uses of
valdiates_uniqueness_ofand autocorrect them to use DB uniqueness validation
Get over with
validates_uniqueness_of and embrace
P.S. I’m the owner of the gem but the topic was copy-pasted from reddit to spread the information about the gem.