Module: ActiveRecord::ConnectionAdapters::MasterSlaveAdapter

Included in:
Mysql2MasterSlaveAdapter, MysqlMasterSlaveAdapter
Defined in:
lib/active_record/connection_adapters/master_slave_adapter.rb,
lib/active_record/connection_adapters/master_slave_adapter/clock.rb,
lib/active_record/connection_adapters/master_slave_adapter/version.rb,
lib/active_record/connection_adapters/master_slave_adapter/circuit_breaker.rb,
lib/active_record/connection_adapters/master_slave_adapter/shared_mysql_adapter_behavior.rb

Defined Under Namespace

Modules: SharedMysqlAdapterBehavior Classes: CircuitBreaker, Clock

Constant Summary collapse

VERSION =
"1.1.2"

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &blk) ⇒ Object

ok, we might have missed more



326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 326

def method_missing(name, *args, &blk)
  master_connection.send(name.to_sym, *args, &blk).tap do
    @logger.try(:warn, %Q{
      You called the unsupported method '#{name}' on #{self.class.name}.
      In order to help us improve master_slave_adapter, please report this
      to: https://github.com/soundcloud/master_slave_adapter/issues

      Thank you.
    })
  end
rescue ActiveRecord::StatementInvalid => exception
  handle_error(master_connection, exception)
end

Class Method Details

.rescued_delegate(*methods) ⇒ Object

ADAPTER INTERFACE DELEGATES ===========================================



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 219

def self.rescued_delegate(*methods)
  options = methods.pop
  to = options[:to]

  file, line = caller.first.split(':', 2)
  line = line.to_i

  methods.each do |method|
    module_eval(<<-EOS, file, line)
      def #{method}(*args, &block)
        begin
          #{to}.__send__(:#{method}, *args, &block)
        rescue ActiveRecord::StatementInvalid => error
          handle_error(#{to}, error)
        end
      end
    EOS
  end
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


180
181
182
183
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 180

def active?
  return true if @disable_connection_test
  connections.map { |c| c.active? }.all?
end

#cache(&blk) ⇒ Object



197
198
199
200
201
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 197

def cache(&blk)
  connections.inject(blk) do |block, connection|
    lambda { connection.cache(&block) }
  end.call
end

#clear_query_cacheObject



209
210
211
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 209

def clear_query_cache
  connections.each { |connection| connection.clear_query_cache }
end

#commit_db_transactionObject



169
170
171
172
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 169

def commit_db_transaction
  on_write { |conn| conn.commit_db_transaction }
  on_commit_callbacks.shift.call(current_clock) until on_commit_callbacks.blank?
end

#connectionsObject



367
368
369
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 367

def connections
  @connections.values.flatten.compact
end

#current_clockObject



375
376
377
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 375

def current_clock
  @master_slave_clock
end

#current_connectionObject



371
372
373
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 371

def current_connection
  connection_stack.first
end

#delete(*args) ⇒ Object



161
162
163
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 161

def delete(*args)
  on_write { |conn| conn.delete(*args) }
end

#disconnect!Object



189
190
191
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 189

def disconnect!
  connections.each { |c| c.disconnect! }
end

#execute(*args) ⇒ Object



165
166
167
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 165

def execute(*args)
  on_write { |conn| conn.execute(*args) }
end

#initialize(config, logger) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 100

def initialize(config, logger)
  super(nil, logger)

  @config = config
  @connections = {}
  @connections[:master] = connect_to_master
  @connections[:slaves] = @config.fetch(:slaves).map { |cfg| connect(cfg, :slave) }
  @last_seen_slave_clocks = {}
  @disable_connection_test = @config[:disable_connection_test] == 'true'
  @circuit = CircuitBreaker.new(logger)

  self.current_connection = slave_connection!
end

#insert(*args) ⇒ Object

ADAPTER INTERFACE OVERRIDES ===========================================



153
154
155
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 153

def insert(*args)
  on_write { |conn| conn.insert(*args) }
end

#master_available?Boolean

Returns:

  • (Boolean)


357
358
359
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 357

def master_available?
  !@connections[:master].nil?
end

#master_clockObject

Raises:

  • (NotImplementedError)


379
380
381
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 379

def master_clock
  raise NotImplementedError
end

#master_connectionObject

UTIL ==================================================================



342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 342

def master_connection
  if circuit.tripped?
    raise MasterUnavailable
  end

  @connections[:master] ||= connect_to_master
  if @connections[:master]
    circuit.success!
    @connections[:master]
  else
    circuit.fail!
    raise MasterUnavailable
  end
end

#on_commit(&blk) ⇒ Object



143
144
145
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 143

def on_commit(&blk)
  on_commit_callbacks.push blk
end

#on_rollback(&blk) ⇒ Object



147
148
149
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 147

def on_rollback(&blk)
  on_rollback_callbacks.push blk
end

#outside_transaction?Boolean

Returns:

  • (Boolean)


213
214
215
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 213

def outside_transaction?
  nil
end

#reconnect!Object



185
186
187
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 185

def reconnect!
  connections.each { |c| c.reconnect! }
end

#reset!Object



193
194
195
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 193

def reset!
  connections.each { |c| c.reset! }
end

#rollback_db_transactionObject



174
175
176
177
178
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 174

def rollback_db_transaction
  on_commit_callbacks.clear
  with(master_connection) { |conn| conn.rollback_db_transaction }
  on_rollback_callbacks.shift.call until on_rollback_callbacks.blank?
end

#slave_clock(conn) ⇒ Object

Raises:

  • (NotImplementedError)


383
384
385
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 383

def slave_clock(conn)
  raise NotImplementedError
end

#slave_connection!Object

Returns a random slave connection Note: the method is not referentially transparent, hence the bang



363
364
365
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 363

def slave_connection!
  @connections[:slaves].sample
end

#uncached(&blk) ⇒ Object



203
204
205
206
207
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 203

def uncached(&blk)
  connections.inject(blk) do |block, connection|
    lambda { connection.uncached(&block) }
  end.call
end

#update(*args) ⇒ Object



157
158
159
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 157

def update(*args)
  on_write { |conn| conn.update(*args) }
end

#with_consistency(clock) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 124

def with_consistency(clock)
  if clock.nil?
    raise ArgumentError, "consistency must be a valid comparable value"
  end

  # try random slave, else fall back to master
  slave = slave_connection!
  conn =
    if !open_transaction? && slave_consistent?(slave, clock)
      slave
    else
      master_connection
    end

  with(conn) { yield }

  current_clock || clock
end

#with_masterObject

MASTER SLAVE ADAPTER INTERFACE ========================================



116
117
118
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 116

def with_master
  with(master_connection) { yield }
end

#with_slaveObject



120
121
122
# File 'lib/active_record/connection_adapters/master_slave_adapter.rb', line 120

def with_slave
  with(slave_connection!) { yield }
end