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 =
['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.



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

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



116
117
118
119
120
# File 'lib/multi_db/connection_proxy.rb', line 116

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



22
23
24
# File 'lib/multi_db/connection_proxy.rb', line 22

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']


29
30
31
# File 'lib/multi_db/connection_proxy.rb', line 29

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.



35
36
37
# File 'lib/multi_db/connection_proxy.rb', line 35

def sticky_slave
  @sticky_slave
end

Instance Attribute Details

#masterObject

Returns the value of attribute master.



15
16
17
# File 'lib/multi_db/connection_proxy.rb', line 15

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.



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

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
  if slaves.empty?
    if self.environment == 'development'
      slaves = [master]
    else
      raise "No slaves databases defined for environment: #{self.environment}"
    end
  end
  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.



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

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



97
98
99
# File 'lib/multi_db/connection_proxy.rb', line 97

def slave
  @slaves.current
end

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



110
111
112
# File 'lib/multi_db/connection_proxy.rb', line 110

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

#with_masterObject



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

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