ShardTheLove - Horizontal Scaling For ActiveRecord

Wow! This takes me back! Please check the date this post was authored, as it may no longer be relevant in a modern context.

Give Real has great execution on technology concepts. We use Rails, Merb and other tools to integrate with a diverse set of APIs. Abstraction and planning for scale are our middle names.

ShardTheLove is a library we use in both Ruby frameworks, Rails and Merb, for scaling our MySQL databases. The code has been humming on our servers for over a year now. There is an alternative library named DataFabric that has similar functionality to ShardTheLove, but the configuration felt clunky and the codebase was getting less maintenance than Mike looks to be giving it today. ShardTheLove is built with support for migrations, testing/RSpec, a flexibility of partitioning patterns, a simple syntax, support for Rails & Merb, and a great name :-) .

Using ShardTheLove

ShardTheLove puts your databases into one of three buckets:

  • System - The default database, and the one where non-sharded data lives.
  • Directory - A database intended for fast queries directing you to the correct shard.
  • Shards - Database servers storing partitioned data.

Partitioning your data by user is a common model. Here’s an example with ShardTheLove randomly choosing a server for your user’s data to live on, storing that location on the directory, then later finding that shard and data:

class User < ActiveRecord::Base
  acts_as_directory

  has_many :reviews

  before_create :choose_shard

private

  def choose_shard
    return unless self.shard.blank?
    self.shard = ShardTheLove.shards[rand(ShardTheLove.shards.length)]
  end

end

class Review < ActiveRecord::Base
  acts_as_shard

  belongs_to :user

end

# Now we can create some reviews.
#

@user = User.create( :email => 'test@example.com' )

ShardTheLove.with(@user.shard) {
  Review.create(
    :user => @user,
    :body => 'Man those aussie meat pies are great!'
}

# And read them back.
#

@user = User.find_by_email( 'test@example.com' )

ShardTheLove.with(@user.shard) {
  @user.reviews
}

If a model calls acts_as_directory or acts_as_shard it behaves as such. If nothing is called, it is assumed to be a model on the system database. This a great way to lazy-configure your app and add sharding without immediately refactoring all your models.

There is documentation up at the ShardTheLove github project page on migrations and testing integration. It’s exciting to get a tool we code with every day out in public! Check back for another post on how ShardTheLove can integrate smoothly with controller actions.