Database based uniqueness validations for ActiveRecord

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.