activeuuid – binary uuid primary keys in Rails 3.1 on MySQL

I’ve been debating a lot recently if I want to use NoSQL (Cassandra) for my next project or if I should just use MySQL. I plan to write a longer post on this soon, but for now I want to share with you a little gem I wrote.

I plan to use NoSQL in the not-too-distance future, but not today. When I port the data over I don’t want to have to map “old ids” to “new ids”. By using a uuid from day one I can easily maintain referential integrity when I migrate to a new datastore.

I looked up how this is typically done in Rails using MySQL and all I could find was examples that store the UUID as strings (e.g. here). The UUID is natively just 16 bytes so storing this in a VARCHAR(36) seemed like a poor solution. Ideally we would store the UUID as binary and let the ORM deal with translating it to and from a UUID object.

activeuuid solves this problem. activeuuid is a Rails plugin that will let you use UUIDTools::UUID objects as primary keys for your ActiveRecords and it stores them in MySQL as binary(16).

Here’s how to use it:

Create a Migration

activeuuid adds the uuid type to your migrations. Example:

class CreateEmails < ActiveRecord::Migration
  def self.up
    create_table :emails, :id => false  do |t|
      t.uuid :id, :unique => true
      t.uuid :sender_id  # belongs_to :sender
 
      t.string :subject
      t.text :body
 
      t.timestamp :sent_at
      t.timestamps
    end
    add_index :emails, :id
  end
 
  def self.down
    drop_table :emails
  end
end

include ActiveUUID::UUID in your model

class Email < ActiveRecord::Base
  include ActiveUUID::UUID
  belongs_to :sender
end

use it

Here are some example specs:

require 'spec_helper'
 
describe Email do
 
  context "when using uuid's as keys" do
    before(:each) do
      Email.delete_all
      @guid = "1dd74dd0-d116-11e0-99c7-5ac5d975667e"
      @e = Email.new(:subject => "hello", :body => "world") {|e| e.id = UUIDTools::UUID.parse(@guid) }
      @e.save
    end
 
    it "the id guid should be equal to the uuid" do
      @e.id.to_s.should eql(@guid)
    end
 
    it "should be able to find an email by the uuid" do
      f = Email.find(UUIDTools::UUID.parse(@guid))
      f.id.to_s.should eql(@guid)
    end
 
  end
end

Benefits

There are a number of benefits to using UUIDs as primary keys.:

  • no id conflict during multi-master write
  • no locking due to auto-increment
  • with time-based UUIDs you can store a timestamp within your UUID
  • you can create natural keys (based on the SHA of model attributes)

Future work

  • more transparent support for natural and composite keys
  • support for MySQLs `INSERT … ON DUPLICATE KEY UPDATE` syntax
  • support a primary column name other than `id`
  • work on other databases (Postgres, etc)
  • tests

Installation

Add this to your Gemfile

    gem "activeuuid"

Or get the code here: https://github.com/jashmenn/activeuuid

References

Dependencies

Note that this depends on Rails 3.1 because it uses the custom column serialization added by Aaron Patterson. (See: https://github.com/rails/rails/commit/ebe485fd8ec80a1a9b86516bc6f74bc5bbba3476)

Share:
  • del.icio.us
  • Reddit
  • Technorati
  • Twitter
  • Facebook
  • Google Bookmarks
  • HackerNews
  • PDF
  • RSS
This entry was posted in code, rails. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.