Class: MultiDb::ConnectionProxy

Inherits:
Object
  • Object
show all
Extended by:
ThreadLocalAccessors
Includes:
ActiveRecord::ConnectionAdapters::QueryCache, QueryCacheCompat
Defined in:
lib/multi_db/connection_proxy.rb

Constant Summary collapse

SAFE_METHODS =

Safe methods are those that should either go to the slave ONLY or go to the current active connection.

[ :select_all, :select_one, :select_value, :select_values, 
:select_rows, :select, :verify!, :raw_connection, :active?, :reconnect!,
:disconnect!, :reset_runtime, :log, :log_info ]
DEFAULT_MASTER_MODELS =

< Rails 2.3

['CGI::Session::ActiveRecordStore::Session']

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from QueryCacheCompat

#columns, #delete, #insert, #select_all, #update

Constructor Details

#initialize(master, slaves) ⇒ ConnectionProxy

Returns a new instance of ConnectionProxy.



86
87
88
89
90
91
92
93
# File 'lib/multi_db/connection_proxy.rb', line 86

def initialize(master, slaves)
  @slaves    = Scheduler.new(slaves)
  @master    = master
  @reconnect = false
  @config    = master.connection.instance_variable_get(:@config)
  self.current      = @slaves.current
  self.master_depth = 0
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

Calls the method on master/slave and dynamically creates a new method on success to speed up subsequent calls



114
115
116
117
118
# File 'lib/multi_db/connection_proxy.rb', line 114

def method_missing(method, *args, &block)
  returning(send(target_method(method), method, *args, &block)) do 
    create_delegation_method!(method)
  end
end

Class Attribute Details

.environmentObject

defaults to RAILS_ENV if multi_db is used with Rails defaults to ‘development’ when used outside Rails



26
27
28
# File 'lib/multi_db/connection_proxy.rb', line 26

def environment
  @environment
end

.master_modelsObject

a list of models that should always go directly to the master

Example:

MultiDb::ConnectionProxy.master_models = ['MySessionStore', 'PaymentTransaction']


33
34
35
# File 'lib/multi_db/connection_proxy.rb', line 33

def master_models
  @master_models
end

.sticky_slaveObject

decides if we should switch to the next reader automatically. If set to false, an after|before_filter in the ApplicationController has to do this. This will not affect failover if a master is unavailable.



39
40
41
# File 'lib/multi_db/connection_proxy.rb', line 39

def sticky_slave
  @sticky_slave
end

Instance Attribute Details

#masterObject

Returns the value of attribute master.



19
20
21
# File 'lib/multi_db/connection_proxy.rb', line 19

def master
  @master
end

Class Method Details

.setup!Object

Replaces the connection of ActiveRecord::Base with a proxy and establishes the connections to the slaves.



43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/multi_db/connection_proxy.rb', line 43

def setup!
  self.master_models ||= DEFAULT_MASTER_MODELS
  self.environment   ||= (defined?(RAILS_ENV) ? RAILS_ENV : 'development')
  self.sticky_slave  ||= false
  
  master = ActiveRecord::Base
  slaves = init_slaves
  raise "No slaves databases defined for environment: #{self.environment}" if slaves.empty?
  master.send :include, MultiDb::ActiveRecordExtensions
  ActiveRecord::Observer.send :include, MultiDb::ObserverExtensions
  master.connection_proxy = new(master, slaves)
  master.logger.info("** multi_db with master and #{slaves.length} slave#{"s" if slaves.length > 1} loaded.")
end

Instance Method Details

#next_reader!Object

Switches to the next slave database for read operations. Fails over to the master database if all slaves are unavailable.



122
123
124
125
126
127
128
# File 'lib/multi_db/connection_proxy.rb', line 122

def next_reader!
  return unless master_depth.zero? # don't if in with_master block
  self.current = @slaves.next
rescue Scheduler::NoMoreItems
  logger.warn "[MULTIDB] All slaves are blacklisted. Reading from master"
  self.current = @master
end

#slaveObject



95
96
97
# File 'lib/multi_db/connection_proxy.rb', line 95

def slave
  @slaves.current
end

#transaction(start_db_transaction = true, &block) ⇒ Object



108
109
110
# File 'lib/multi_db/connection_proxy.rb', line 108

def transaction(start_db_transaction = true, &block)
  with_master { @master.retrieve_connection.transaction(start_db_transaction, &block) }
end

#with_masterObject



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

def with_master
  self.current = @master
  self.master_depth += 1
  yield
ensure
  self.master_depth -= 1
  self.current = slave if master_depth.zero?
end