Firstly, can anyone explain how unique index works in databases?
Suppose I have a User model with a name column
and I add a unique index
on it
Here are the difference between unique index and validates_uniqueness_of
This is a patch to enable ActiveRecord to identify db-generated errors for unique constraint violations. For example, it makes the following work without declaring a validates_uniqueness_of:
create_table "users" do |t|
t.string "email", null: false
end
add_index "users", ["email"], unique: true
class User < ActiveRecord::Base
end
User.create!(email: 'abc@abc.com')
u = User.create(email: 'abc@abc.com')
u.errors[:email]
=> "has already been taken"
The benefits are speed, ease of use, and completeness --
Speed
With this approach you don't need to do a db lookup to check for uniqueness when saving (which can sometimes be quite slow when the index is missed -- https://rails.lighthouseapp.com/projects/8994/tickets/2503-validate... ). If you really care about validating uniqueness you're going to have to use database constraints anyway so the database will validate uniqueness no matter what and this approach removes an extra query. Checking the index twice isn't a problem for the DB (it's cached the 2nd time around), but saving a DB round-trip from the application is a big win.
Ease of use
Given that you have to have db constraints for true uniqueness anyway, this approach will let everything just happen automatically once the db constraints are in place. You can still use validates_uniqueness_of if you want to.
Completeness
validates_uniqueness_of has always been a bit of a hack -- it can't handle race conditions properly and results in exceptions that must be handled using somewhat redundant error handling logic. (See "Concurrency and integrity" section in http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMe...)
validates_uniqueness_of is not sufficient to ensure the uniqueness of a value. The reason for this is that in production, multiple worker processes can cause race conditions:
Two concurrent requests try to create a user with the same name (and we want user names to be unique)
The requests are accepted on the server by two worker processes who will now process them in parallel
Both requests scan the users table and see that the name is available
Both requests pass validation and create a user with the seemingly available name
For more clear understanding please check this
If you create a unique index for a column it means you’re guaranteed the table won’t have more than one row with the same value for that column. Using only validates_uniqueness_of validation in your model isn’t enough to enforce uniqueness because there can be concurrent users trying to create the same data.
Imagine that two users tries to register an account with the same email where you have added validates_uniqueness_of :email in your user model. If they hit the “Sign up” button at the same time, Rails will look in the user table for that email and respond back that everything is fine and that it’s ok to save the record to the table. Rails will then save the two records to the user table with the same email and now you have a really shitty problem to deal with.
To avoid this you need to create a unique constraint at the database level as well:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
...
end
add_index :users, :email, unique: true
end
end
So by creating the index_users_on_email unique index you get two very nice benefits. Data integrity and good performance because unique indexes tends to be very fast.
If you put unique: true in your posts table for user_id then it will not allow to enter duplicate records with same user_id.