Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Handling Uniqueness and Duplicates in Rails

Handling Uniqueness and Duplicates in Rails

The uniqueness validation provided by Rails is not enough to ensure you don't get duplicates. Adding indices causes 500s. This presentation can help!

Guillaume Malette

March 01, 2016
Tweet

More Decks by Guillaume Malette

Other Decks in Programming

Transcript

  1. do not duplicate This talk is called “Do not duplicate”

    We’ll explore the pitfalls to avoid when dealing with uniqueness constraints in Rails
  2. [Rails] User.create(email: "[email protected]") [SQL] SELECT 1 AS one FROM `users`

    WHERE (`users`.`email` = '[email protected]') LIMIT 1 [SQL] #=> 0 [Rails] [Rails] INSERT INTO `users` (`email`) VALUES ('[email protected]') Rails performs a SELECT to see that no other record fit the uniqueness constraint
  3. [Rails] User.create(email: “[email protected]”) [SQL] SELECT 1 AS one FROM `users`

    WHERE (`users`.`email` = '[email protected]') LIMIT 1 [SQL] #=> 0 [Rails] [Rails] INSERT INTO `users` (`email`) VALUES ('[email protected]') Worker 1 [Rails] User.create(email: "[email protected]") [SQL] SELECT 1 AS one FROM `users` WHERE (`users`.`email` = '[email protected]') LIMIT 1 [SQL] #=> 0 [Rails] [Rails] INSERT INTO `users` (`email`) VALUES ('[email protected]') Worker 2 [Rails] [Rails] Two simultaneous requests trying to create the same user
  4. change_table :users do |m| m.index :email, unique: true end To

    fix this we’ll add a uniqueness constraint in the database
  5. [Rails] User.create(email: “[email protected]”) [SQL] SELECT 1 AS one FROM `users`

    WHERE (`users`.`email` = '[email protected]') LIMIT 1 [SQL] #=> 0 [Rails] [Rails] INSERT INTO `users` (`email`) VALUES ('[email protected]') Worker 1 [Rails] User.create(email: "[email protected]") [SQL] SELECT 1 AS one FROM `users` WHERE (`users`.`email` = '[email protected]') LIMIT 1 [SQL] #=> 0 [Rails] [Rails] INSERT INTO `users` (`email`) VALUES ('[email protected]') Worker 2 [Rails]
  6. 500

  7. rescue_from ActiveRecord::RecordNotUnique do # Do something sane end The first

    thing one can do is add this to the controller I don’t like `rescue_from`
  8. [Rails] User.create(email: “[email protected]”) [SQL] SELECT 1 AS one FROM `users`

    WHERE (`users`.`email` = '[email protected]') LIMIT 1 [SQL] #=> 0 [Rails] [Rails] INSERT INTO `users` (`email`) VALUES ('[email protected]') Worker 1 [Rails] User.create(email: "[email protected]") [SQL] SELECT 1 AS one FROM `users` WHERE (`users`.`email` = '[email protected]') LIMIT 1 [SQL] #=> 0 [Rails] [Rails] INSERT INTO `users` (`email`) VALUES ('[email protected]') Worker 2 [Rails] [Rails]
  9. config.eager_load = true RescueFromDuplicate.missing_unique_indexes It comes with a few other

    utilities, like helping you find missing unique indexes in your app