Module: ReadFromSlave

Defined in:
lib/read_from_slave.rb,
lib/read_from_slave/railtie.rb

Overview

Read_from_slave for Rails enables database reads from one or more slave databases, while writes continue to go to the master To use read_from_slave you must install the gem, configure the gem in your environment file, and setup your database.yml file with an entry for your slave database.

gem install read_from_slave

Read_from_slave is compatible with Rails 2.2.x and Rails 3

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:
    slave_1:  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

Note that if you have multiple databases you can also configure multiple slaves.

References

** not thread safe ** won't work with apps that talk to multiple (master) databases

** old, not suitable for Rails 2.x

** similar to read_from_slave, but adapter based approach

** 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

Defined Under Namespace

Modules: CalculationMethod, ClassMethods, InstanceMethods Classes: Railtie

Constant Summary collapse

@@all_reads_on_slave =
true

Class Method Summary collapse

Class Method Details

.all_reads_on_slaveObject


114
115
116
# File 'lib/read_from_slave.rb', line 114

def all_reads_on_slave
  @@all_reads_on_slave
end

.all_reads_on_slave=(all_reads) ⇒ Object


109
110
111
112
# File 'lib/read_from_slave.rb', line 109

def all_reads_on_slave=(all_reads)
  @@all_reads_on_slave = all_reads
  default_to_master!
end

.default_to_master!Object


99
100
101
102
103
104
105
106
# File 'lib/read_from_slave.rb', line 99

def default_to_master!
  base = ActiveRecord::Base
  base.class_eval do
    class << self
      alias_method_chain :connection, :slave_db_scope unless ReadFromSlave.all_reads_on_slave
    end
  end
end

.install!Object


59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/read_from_slave.rb', line 59

def install!
  base = ActiveRecord::Base
  base.send(:include, InstanceMethods)
  base.alias_method_chain :reload, :read_from_slave
  base.extend(ClassMethods)
  base.class_eval do
    class << self
      alias_method_chain :find_by_sql, :read_from_slave
      alias_method_chain :connection, :read_from_slave
    end
  end

  begin
    calculation_base = ActiveRecord::Relation  # rails 3
    calculation_base.send(:include, CalculationMethod)
    calculation_base.alias_method_chain :calculate, :read_from_slave
  rescue NameError  # rails 2
    base.extend(CalculationMethod)
    base.class_eval do
      class << self
        alias_method_chain :calculate, :read_from_slave
      end
    end
  end
end

.install_with_methods!Object


85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/read_from_slave.rb', line 85

def install_with_methods!
  ActiveRecord::Base.connection.instance_variable_get(:@config)[:slaves].each_key do |slave_name|
    ActiveRecord::Base.class_eval <<-EOM
      def self.with_#{slave_name}(&block)
        Thread.current[:with_#{slave_name}_count] ||= 0
        Thread.current[:with_#{slave_name}_count] += 1
        yield
      ensure
        Thread.current[:with_#{slave_name}_count] -= 1
      end
    EOM
  end if ActiveRecord::Base.connection.instance_variable_get(:@config)[:slaves]
end