class ActiveRecord::Bogacs::Validator

Every frequency seconds, the validator will perform connection validation on a pool it operates.

Configure the frequency by setting ‘:validate_frequency` in your AR configuration.

We recommend not setting values too low as that would drain the pool’s performance under heavy concurrent connection retrieval. Connections are also validated upon checkout - the validator is intended to detect long idle pooled connections “ahead of time” instead of upon retrieval.

@note Do not use a reaper with the validator! Reaping (stale connection detection and removal) is part of the validation process and will only slow things down as all pool connection updates needs to be synchronized.

Constants

TimerTask

Attributes

frequency[R]
pool[R]
timeout[R]

Public Class Methods

new(pool, frequency = 60, timeout = nil) click to toggle source

‘Validator.new(pool, spec.config).run` @private

# File lib/active_record/bogacs/validator.rb, line 30
def initialize(pool, frequency = 60, timeout = nil)
  @pool = pool; PoolAdaptor.adapt! pool
  if frequency # validate every 60s by default
    frequency = frequency.to_f
    @frequency = frequency > 0.0 ? frequency : false
  else
    @frequency = nil
  end
  if timeout
    timeout = timeout.to_f
    @timeout = timeout > 0.0 ? timeout : 0
  else
    @timeout = @frequency
  end
  @running = nil
end

Public Instance Methods

run() click to toggle source
# File lib/active_record/bogacs/validator.rb, line 47
def run
  return unless frequency
  @running = true; start
end
running?() click to toggle source
# File lib/active_record/bogacs/validator.rb, line 61
def running?; @running end
start() click to toggle source
# File lib/active_record/bogacs/validator.rb, line 55
def start
  TimerTask.new(:execution_interval => frequency, :timeout_interval => timeout) do
    validate_connections
  end
end
validate() click to toggle source
# File lib/active_record/bogacs/validator.rb, line 63
def validate
  start = Time.now
  conns = connections
  logger && logger.debug("[validator] found #{conns.size} candidates to validate")
  invalid = 0
  conns.each { |connection| invalid += 1 if validate_connection(connection) == false }
  logger && logger.info("[validator] validated pool in #{Time.now - start}s (removed #{invalid} connections from pool)")
  invalid
end

Private Instance Methods

connections() click to toggle source
# File lib/active_record/bogacs/validator.rb, line 75
def connections
  connections = pool.connections.dup
  connections.map! do |conn|
    if conn
      if owner = conn.owner
        if owner.alive?
          nil # owner.alive? ... do not touch
        else # stale-conn (reaping)
          pool.remove conn # remove is synchronized
          conn.disconnect! rescue nil
          nil
        end
      elsif conn.in_use? # no owner? (likely a nasty bug)
        logger && logger.warn("[validator] found in-use connection ##{conn.object_id} without owner - removing from pool")
        pool.remove_without_owner conn # synchronized
        conn.disconnect! rescue nil
        nil
      else
        conn # conn not in-use - candidate for validation
      end
    end
  end
  connections.compact
end
logger() click to toggle source

def synchronize(&block); pool.synchronize(&block) end

# File lib/active_record/bogacs/validator.rb, line 124
def logger
  @logger ||= ( pool.respond_to?(:logger) ? pool.logger : nil ) rescue nil
end
validate_connection(conn) click to toggle source
# File lib/active_record/bogacs/validator.rb, line 100
def validate_connection(conn)
  return nil if conn.in_use?
  pool.synchronize do # make sure it won't get checked-out while validating
    return nil if conn.in_use?
    # NOTE: active? is assumed to behave e.g. connection_alive_timeout used
    # on AR-JDBC active? might return false as the JDBC connection is lazy
    # ... but that is just fine!
    begin
      return true if conn.active? # validate the connection - ping the DB
    rescue => e
      logger && logger.info("[validator] connection ##{conn.object_id} failed to validate: #{e.inspect}")
    end

    # TODO support seconds_idle - only validate if certain amount since use passed

    logger && logger.debug("[validator] found non-active connection ##{conn.object_id} - removing from pool")
    pool.remove_without_owner conn # not active - remove
    conn.disconnect! rescue nil
    return false
  end
end