Skip to content

Rodrigo Urubatan – About Code

Helping ruby developers to use the best tools for each job so they can solve hard problems, with less bugs and have more free time.

Menu
  • Home
  • My last presentations
  • About
  • Privacy Policy
Menu

Do not trust ActiveRecord data consistency validations, delegate that to the database

Posted on 2018-10-15 by Rodrigo Urubatan

Of course you can still use AR validations, that is not my point. 

But data consistency related validations should not trust AR for that, for example, if you have a “unique” constraint, and you are just trusting the database, it is really easy to break the DB consistency if for some reason you have a long transaction (long varies according to your concurrency, infrastructure, …)

For example, lets assume the following scenario:

You have a User model with a unique constraint on the username, and every time a new user is added to the system you need to send a validation email, the “create user” transaction is only completed after the email is sent.

Today, the email delivery system is overloaded for some random reason and email deliveries are taking 2 seconds, instead of the usual few milliseconds.

Your user model would be something like this:

class User < ActiveRecord::Base
  validates username: uniqueness: true
end

Your database script would be something like:

create_table :users do |t|
  t.string :username
end

And you have this transaction:

User.transaction do
  @user = User.create! params[:username]
  UserEmail.signup_email(@user).deliver!
end

This code usually works, but since today your email delivery server is under heavy load, and you are very unlucky, because two john.doe just clicked “Signup” within 1 second of each other.

Now you have two duplicated usernames in your system.

But if your migration was:

create_table :users do |t|
  t.string :username
  t.index :username, unique: true
end

You would have no problem at all, because the database system can handle it very well.

But what is the problem with the first code?

It is simple, inside the transaction, when rails query for the data checking if there is no conflict for the username field, the data from the other transaction is not there yet.

But when the database has the validation, and the data is inserted in one transaction, the other transaction that is trying to write to the same “key” is locked and needs to wait, so the data is always valid.

This of course will not happen for small transactions with only one statement, but once you need a bigger transaction you are in trouble if you kept your validations only in the AR model.

Related

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent posts

  • Why Embrace Nesting in Ruby Modules?
  • An easy way to have a local “Github Copilot” for free
  • SPA without touching Javascript – The magic of Ruby on rails and Hotwire
  • I see Dead Jobs everywhere (sidekiq DeadSet)
  • Quick tips that help: rails notes

Arquives

  • May 2024
  • April 2024
  • February 2023
  • January 2023
  • December 2022
  • June 2021
  • March 2020
  • January 2020
  • July 2019
  • June 2019
  • May 2019
  • October 2018
  • September 2018
  • August 2018
  • July 2018
  • June 2018
  • May 2018
  • February 2018
  • January 2018
  • November 2017
  • August 2015
  • August 2014
  • July 2014
  • August 2007

Categories

  • AI
  • articles
  • cfp
  • firebase
  • gems
  • git
  • opinion
  • presentations
  • projects
  • rails6
  • ruby
  • Sem categoria
  • server-api
  • tutorials
  • Uncategorized
© 2025 Rodrigo Urubatan – About Code | Powered by Minimalist Blog WordPress Theme