Postamt is a sane, Rails-4-ready solution for performing database reads against a hot standby server.
Ruby HTML JavaScript CSS
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib
testapp41
testapp42
.gitignore
CHANGELOG.md
Gemfile
LICENSE.txt
README.md
Rakefile
postamt.gemspec

README.md

Postamt

Gem Version

Postamt is a sane, solution for performing database reads against a hot standby server with Rails 4.1 and 4.2.

If you use Rails 3.2 or 4.0, use Postamt version 0.9.2.

Choose per model and/or controller&action whether a read-only query should be sent to master or a hot standby.
Inside a transaction reads always happen against master.

Care has been taken to avoid common performance pitfalls. It's been battle tested in production at sauspiel.de.

Monkey-patching is kept to an absolute minimum, the hard work happens through officially-supported Rails APIs. That's why there's so little code compared to similar gems.

Postamt requires Rails 3.2+ and works with Rails 4.

Installation

Add this line to your application's Gemfile:

gem 'postamt'

Example usage

# database.yml
development:
  adapter: postgresql
  database: app
  username: app
  password:
  host: master.db.internal
  encoding: utf8
  slave:
    host: slave.db.internal
    username: app_readonly
class UserController < ApplicationController
  use_db_connection :slave, for: ['User'], only: [:search]

  def search
    # SELECTs here are sent to slave
    # User#save and User.create would be sent to master anyways.
    # Everything in a transaction block too.
    @users = User.where(...) # sent to slave
    @something_else = SomethingElse.first # sent to master
  end

  def create
    @user = User.new(params[:user])
    @user.save! # sent to master
  end

  def invoice
    transaction do
      @user = User.where(...) # sent to master
      @invoices = Invoice.create(...) # sent to master
    end
  end
end
class ArchivedItem < ActiveRecord::Base
  # default_connection can be overwritten with 
  # * Postamt.on(...) { ... },
  # * ActiveRecord::Base.transaction { ... }, and
  # * use_db_connection :other_connection, for: ['ArchivedItem'] in a controller.
  self.default_connection = :slave
end

User.where(...) # sent to master
item = ArchivedItem.where(...) # sent to slave
item.title = "changed title"
item.save! # sent to master
item.reload # sent to slave, beware of replication lag here!

ActiveRecord::Base.transaction do
  ArchivedItem.where(...) # sent to master, since we're in a transaction
  User.where(...) # sent to master
end

Postamt.on(:master) do
  ArchivedItem.where(...) # sent to master
  User.where(...) # sent to master
end
# If you don't want to test with a slave DB put this in config/environments/test.rb
Postamt.force_connection = :master

Tests

Create the DB postamt_test and ensure the users master and slave exist:

$ createdb postamt_test
$ createuser -s master # -s => superuser
$ createuser -s slave # better to restrict slave to be read-only

Migrate the DB in the Rails 4 app:

$ cd testapp41 # Rails 4.1
$ RAILS_ENV=test bundle exec rake db:migrate
$ bundle exec ruby -Itest test/integration/postamt_test.rb

You can't run the tests via a simple rake because Postamt deactivates itself when it detects that a task starting with 'db' is run (like db:test:prepare)

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request