Read_from_slave

Read_from_slave for Rails enables database reads from one or more slave databases, while writes continue to go to the master

Read_from_slave will work with Rails 2.2 and above, including Rails 3 versions.

Note that this version (0.5.x) supports multiple slave databases, but this caused an incompatibility in the configuration - your database.yml file must be updated before you can use this version.

Installation

gem install read_from_slave

Configuration

In config/environments/production.rb (for instance)

config.gem "read_from_slave"

In config/database.yml

production:
  adapter: mysql
  database: mydatabase
  username: myuser
  password: mypassword
  host: my.main.database.server.com
  port: 3306
  slaves:
    primary_slave:  slave_for_reads
    slave_2:  slave_for_reporting

slave_for_reads:
  adapter: mysql
  database: mydatabase
  username: myuser
  password: mypassword
  socket: /var/lib/mysql/mysql.sock

slave_for_reporting:
  adapter: mysql
  database: mydatabase
  username: myuser
  password: mypassword
  host: my.slave.database.server.com

Just use the regular YAML format to specify your slave database, it could equally well be on another server as the local example given above.

In the example above, primary_slave and slave_2 are names that are used to generate methods you can use to specify which slave to use. slave_for_reads and slave_for_reporting is the yaml key that is used to make the connection to your slave database.

You can have any number of slaves configured. To connect to the slaves:

ActiveRecord::Base.with_primary_slave do

User.last

end

ActiveRecord::Base.with_slave_2 do

User.last

end

with_primary_slave and with_slave_2 are dynamically created based on the keys provided in the yaml file. Everything inside the block with read from the corresponding slave database specified in the yaml file.

You must have one slave with the primary_slave key. By default, all the reads will occur on the primary_slave, in the example above, and not require you to use the with_primary_slave method. You actually never need to use the with_primary_slave method as that slave will alwaysbe used if not inside a with… method block. So if you only have 1 slave, you don’t ever need to worry about the with… methods.

If you have multiple slaves, you will want to make sure you specify slave configurations for each environment, even if you don’t have slaves in every environment.

development:

adapter: mysql
database: mydatabase
username: myuser
password: mypassword
host: my.main.database.server.com
port: 3306
slaves:
  primary_slave:  development
  slave_2:  development

The reason is, your code will have with_primary_slave and with_slave_2 methods because that is what you need in production, but you will get undefined method errors in development because they won’t be generated unless your development configuration specifies slaves. Just specifying the slave keys back to the development configuration should serve your needs.

You may have a need where you don’t actually want all of your reads to be on the slave. You may want to be much more selective in what you put on the slave. For instance, perhaps you want everything on the master except large reports. You can change the default behavior of read_from_slave by setting:

ReadFromSlave.all_reads_on_slave = false

in your environment.rb file. This will make all reads on the master by default and reads will only be on the slave inside the with… method blocks.

Phusion Passenger

Note that if you are using Passenger, you need to make sure that the slave database is reconnected if there is any chance that it was accessed before the spawner forks. This could be because database was accessed during the generation of routes, or perhaps if you are using the has_many_polymorphs gem.

The safest thing to do is to have something like this in your production.rb or environment.rb:

PhusionPassenger.on_event(:starting_worker_process) do |forked|
  if forked
    # We're in smart spawning mode.
    ActiveRecord::Base.establish_slave_connections
  else
    # We're in conservative spawning mode. We don't need to do anything.
  end
end

Documentation

rdoc.info/github/sdsykes/read_from_slave/master/frames

Tests

Clone the git repository, and you can run the read_from_slave tests or entire ActiveRecord test suite to prove that read_from_slave works correctly.

$ rake test
...snip..
Finished in 0.046365 seconds.

7 tests, 7 assertions, 0 failures, 0 errors

$ rake test_with_active_record
...snip...
Finished in 51.904306 seconds.

2057 tests, 6685 assertions, 0 failures, 0 errors

References

“Masochism”:github.com/technoweenie/masochism/tree/master not thread safe won’t work with apps that talk to multiple (master) databases

“Acts as readonlyable”:rubyforge.org/projects/acts-as-with-ro/ old, not suitable for Rails 2.x

“master_slave_adapter”:github.com/mauricio/master_slave_adapter/tree/master similar to read_from_slave, but adapter based approach

“multi_db”:github.com/schoefmax/multi_db/tree/master another one, proxy connection approach looks like it won’t work with apps that talk to multiple (master) databases more complex than read_from_slave

© 2009-2011 Stephen Sykes Thanks to Kevin Tyll for contributing the multiple slave feature