Rails: Special Use Cases Of MD5

What is MD5?

The MD5 is a hashing algorithm. It is a one-way cryptographic function. It accepts a message of any length as input and returns the fixed-length digest value.

There are various use-cases of MD5. I have listed few of them below.

Use-case 1: Single Uniqueness Validation For Multiple Attributes.

Have you ever stuck in a situation, where you need to execute multiple unique validations to insert single record in database?

These validations could be present in same model or different models. In that scenario adding uniqueness validation on each attribute tends to performance degradation. you’ll face this performance issue more often, if you have multiple records in database.

Lets consider a scenario:

  • 2 Models: Product and Size.
  • Association between models: Product has many Sizes.
  • Uniqueness Validations We Need:
    1. For product, SKU and colour_code combination should be unique.
    2. For size, product.sku, product.colour_code, size, attribute_size and source combination should be unique.

Standard approach could be as following:

app/models/product.rb

class Product < ApplicationRecord
  # field :sku
  # field :colour_code
  # field :category
  # field :name
  # field :gender
  # field :price  

  has_many :sizes
  
  validates :sku, uniqueness: {scope: [:colour_code]}
end

app/models/size.rb

class Size < ApplicationRecord
  # field :size
  # field :attr_size
  # field :source  

belongs_to :product  

validates :size, :attribute_size, :source,
    uniqueness: {scope: [:product_id]}
end

Now when inserting/updating size records, database validates all attributes and eventually it will impact performance.

Now, we need a solution!

We can use only one MD5 attribute for uniqueness validation.

So lets add one new attribute on size modal:

class AddMd5ToSizes < ActiveRecord::Migration[5.2]
  def change
    add_column :sizes, :md5, :string
  end
end

And next step is to add logic for MD5 attribute.

class Size < ApplicationRecord
  ...
  ...  
  before_save do |record|
    record.set_md5
  end

  private 

  def set_md5
    _md5 = Digest::MD5.new
    _md5.update "#{product.sku}#{product.colour_code}#{size}"\
                "#{attribute_size}#{source}".downcase
    self.md5 = _md5.hexdigest
  end
end

add_index :companies_releases, [:company_id, :release_id], unique: trueAt the end, we’ll just add only one validation.

class Size < ApplicationRecord
  # field :size
  # field :attr_size
  # field :source
  # field :md5  

  belongs_to :product  

  validates :md5, uniqueness: true
end

Ta…da….! It’s done

Let’s have a look at another use case of MD5.

Use-case 2: Indexing On Variable Length Attributes.

Lets consider one scenario. Suppose we want to store profile image URL in user model. Now our attribute is profile_url.

class User< ApplicationRecord
  # field :first_name
  # field :last_name
  # field :profile_url
  ...
end

Now, our case is, we want to add unique index on profile_url attribute. As we know URL can be of variable length and it can vary from say 10 characters to 1000 characters, or more.

Any index on attribute with variable length, is very heavy operation, in terms of performance.

As a solution to such cases, we can take help of MD5, to add index.

Same as use-case 1, add new attribute to the model, to store MD5 hash of URL. It is of fixed size, unique and gives better performance for index heavy operations.

# db/migrate/20200131101209_add_md5_users.rb
class AddMd5ToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :md5, :string
    add_index :users, :md5, unique: true
  end
end

# app/models/user.rb
class User < ApplicationRecord
  ...
  ...
  before_save do |record|
    record.set_md5
  end  

  private 

  def set_md5
    _md5 = Digest::MD5.new
    _md5.update "#{profile_url}"
    self.md5 = _md5.hexdigest
  end
end

In this way, with the help of MD5, we can easily add uniqueness validation on such multiple and nested attributes. Also we saw, how to add index of variable sized attributes.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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