Managing uniqueness on self generated field

Recently I ran into a common problem for a Rails developer: how to manage uniqueness on a self generated field.
The problem was pretty simple, in my Rails app I’ve got a resource that contains two different attributes generated during resource creation. These attributes uuid, access_token are provided to the user in order to interact with an API. As tokens I’ve chosen to use UUID standard format.
SecureRandom.uuid, integrated with Ruby, aims to generate UUID but does not guarantee complete uniqueness so my goal was to avoid uniqueness collision at resource creation.

Here’s resource (Feed) migration:

class CreateFeeds < ActiveRecord::Migration
  def change
    create_table :feeds do |t|
      t.uuid :uuid, null: false
      t.uuid :access_token, null: false
      t.string :name, null: false
      t.string :time_zone, default: 'UTC', null: false

      t.timestamps null: false

    add_index :feeds, :uuid, unique: true
    add_index :feeds, :access_token, unique: true

My first approach to solve the problem was to manage uniqueness using Rails validations, loops and hooks.

class Feed < ActiveRecord::Base
  validates :uuid, presence: true
  validates :access_token, presence: true

  before_validation :generate_uuid, unless: :uuid?
  before_validation :generate_access_token, unless: :access_token?


  def generate_uuid
      self.uuid = SecureRandom.uuid
    end while self.class.exists?(uuid: uuid)

  def generate_access_token
      self.access_token = SecureRandom.uuid
    end while self.class.exists?(access_token: access_token)

As you can see in the log below, this solution brings the disadvantage of multple query on the database to check the presence of the generated UUIDs.

2.3.0 :001 > Feed.create(name: 'Example')

(0.1ms)  BEGIN
Feed Exists (14.7ms)  SELECT  1 AS one FROM "feeds" WHERE "feeds"."uuid" = $1 LIMIT 1  [["uuid", "1be12f2a-23f0-49f9-bac8-456c09fcede6"]]
Feed Exists (5.7ms)  SELECT  1 AS one FROM "feeds" WHERE "feeds"."access_token" = $1 LIMIT 1  [["access_token", "9b773024-741f-4904-8718-a6b68eb8b389"]]
SQL (3.6ms)  INSERT INTO "feeds" ("name", "uuid", "access_token", "time_zone", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"  [["name", "Example"], ["uuid", "1be12f2a-23f0-49f9-bac8-456c09fcede6"], ["access_token", "9b773024-741f-4904-8718-a6b68eb8b389"], ["time_zone", "UTC"], ["created_at", "2016-04-25 09:57:34.845915"], ["updated_at", "2016-04-25 09:57:34.845915"]]
(135.0ms)  COMMIT

After several attepts I’ve found out that the best solution was to let broke resource creation and manage ActiveRecord::RecordNotUnique.

class Feed < ActiveRecord::Base
  UNIQUE_FIELDS = %w[uuid access_token]

  before_save :assign_uuid, :assign_access_token
  around_save :save_managing_uniqueness


  def save_managing_uniqueness
    self.class.connection.execute('SAVEPOINT before_record_create;')
    self.class.connection.execute('RELEASE SAVEPOINT before_record_create;')
  rescue ActiveRecord::RecordNotUnique => error
    @attempts = @attempts.to_i + 1
    if @attempts < 3
      duplicated_field = error.message[/Key ((w+))/, 1]
      send("assign_#{duplicated_field}", true) if UNIQUE_FIELDS.include?(duplicated_field)
      self.class.connection.execute('ROLLBACK TO SAVEPOINT before_record_create;')
      raise error, 'Retries exhausted'

  def assign_uuid(force = false)
    self.uuid = SecureRandom.uuid if uuid.nil? || force

  def assign_access_token(force = false)
    self.access_token = SecureRandom.uuid if access_token.nil? || force

Be aware to change duplicated_field = error.message[/Key ((w+))/, 1] if you’re not working with Postgres.

I’m not fully convinced of using a regex to find what attribute raises duplication error, feel free to improve this part. The idea was to give a working example of rescue in a Rails transaction.

Leave a Reply

Please Login to comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.