class RailsFailover::ActiveRecord::Handler

Constants

VERIFY_FREQUENCY_BUFFER_PRECENT

Public Class Methods

new() click to toggle source
Calls superclass method
# File lib/rails_failover/active_record/handler.rb, line 14
def initialize
  @primaries_down = Concurrent::Map.new

  super() # Monitor#initialize
end

Public Instance Methods

primaries_down_count() click to toggle source
# File lib/rails_failover/active_record/handler.rb, line 34
def primaries_down_count
  primaries_down.size
end
primary_down?(handler_key) click to toggle source
# File lib/rails_failover/active_record/handler.rb, line 30
def primary_down?(handler_key)
  primaries_down[handler_key]
end
verify_primary(handler_key) click to toggle source
# File lib/rails_failover/active_record/handler.rb, line 20
def verify_primary(handler_key)
  primary_down(handler_key)

  mon_synchronize do
    return if @thread&.alive?
    logger.warn "Failover for ActiveRecord has been initiated"
    @thread = Thread.new { loop_until_all_up }
  end
end

Private Instance Methods

all_primaries_up() click to toggle source
# File lib/rails_failover/active_record/handler.rb, line 91
def all_primaries_up
  primaries_down.empty?
end
initiate_fallback_to_primary() click to toggle source
# File lib/rails_failover/active_record/handler.rb, line 51
def initiate_fallback_to_primary
  frequency = RailsFailover::ActiveRecord.verify_primary_frequency_seconds
  sleep(frequency * ((rand(VERIFY_FREQUENCY_BUFFER_PRECENT) + 100) / 100.0))

  active_handler_keys = []

  primaries_down.keys.each do |handler_key|
    connection_handler = ::ActiveRecord::Base.connection_handlers[handler_key]

    connection_pool = connection_handler.retrieve_connection_pool(spec_name)
    if connection_pool.respond_to?(:db_config)
      config = connection_pool.db_config.configuration_hash
      adapter_method = connection_pool.db_config.adapter_method
    else
      config = connection_pool.spec.config
      adapter_method = connection_pool.spec.adapter_method
    end
    logger.debug "#{Process.pid} Checking server for '#{handler_key} #{spec_name}'..."
    connection_active = false

    begin
      connection = ::ActiveRecord::Base.public_send(adapter_method, config)
      connection_active = connection.active?
    rescue => e
      logger.debug "#{Process.pid} Connection to server for '#{handler_key} #{spec_name}' failed with '#{e.message}'"
    ensure
      connection.disconnect! if connection
    end

    if connection_active
      logger.debug "#{Process.pid} Server for '#{handler_key} #{spec_name}' is active."
      active_handler_keys << handler_key
    end
  end

  active_handler_keys.each do |handler_key|
    primary_up(handler_key)
  end
end
logger() click to toggle source
# File lib/rails_failover/active_record/handler.rb, line 123
def logger
  ::Rails.logger
end
loop_until_all_up() click to toggle source
# File lib/rails_failover/active_record/handler.rb, line 40
def loop_until_all_up
  loop do
    initiate_fallback_to_primary

    if all_primaries_up
      logger.warn "Fallback to primary for ActiveRecord has been completed."
      break
    end
  end
end
primaries_down() click to toggle source
# File lib/rails_failover/active_record/handler.rb, line 109
def primaries_down
  ancestor_pids = nil
  value = @primaries_down.compute_if_absent(Process.pid) do
    ancestor_pids = @primaries_down.keys
    @primaries_down.values.first || Concurrent::Map.new
  end

  ancestor_pids&.each do |pid|
    @primaries_down.delete(pid)&.each_key { |key| verify_primary(key) }
  end

  value
end
primary_down(handler_key) click to toggle source
# File lib/rails_failover/active_record/handler.rb, line 95
def primary_down(handler_key)
  already_down = primaries_down.put_if_absent(handler_key, true)
  RailsFailover::ActiveRecord.on_failover_callback!(handler_key) if !already_down
end
primary_up(handler_key) click to toggle source
# File lib/rails_failover/active_record/handler.rb, line 100
def primary_up(handler_key)
  already_up = !primaries_down.delete(handler_key)
  RailsFailover::ActiveRecord.on_fallback_callback!(handler_key) if !already_up
end
spec_name() click to toggle source
# File lib/rails_failover/active_record/handler.rb, line 105
def spec_name
  ::ActiveRecord::Base.connection_specification_name
end