Skip to content

Read_from_slave for Rails enables database reads from a slave database, while writes continue to go to the master

License

Notifications You must be signed in to change notification settings

sdsykes/read_from_slave

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

66 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

http://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
    • not thread safe
    • won’t work with apps that talk to multiple (master) databases
  • Acts as readonlyable
    • old, not suitable for Rails 2.x
  • master_slave_adapter
    • similar to read_from_slave, but adapter based approach
  • multi_db
    • 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 Stephen Sykes
Thanks to Kevin Tyll for contributing the multiple slave feature

About

Read_from_slave for Rails enables database reads from a slave database, while writes continue to go to the master

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages