class ActiveRecord::OverflowSignalizer

Constants

DAY
DEFAULT_AVG
MAX_VALUE
VERSION

Public Class Methods

new(logger: nil, models: nil, days_count: 60, signalizer: nil) click to toggle source
# File lib/activerecord/overflow_signalizer.rb, line 17
def initialize(logger: nil, models: nil, days_count: 60, signalizer: nil)
  @logger = logger || ActiveRecord::Base.logger
  @models = models || ActiveRecord::Base.descendants
  @days_count = days_count
  @signalizer = signalizer
end

Public Instance Methods

analyse() click to toggle source
# File lib/activerecord/overflow_signalizer.rb, line 49
def analyse
  analyse!
rescue Overflow => e
  signalize(e.message)
end
analyse!() click to toggle source
# File lib/activerecord/overflow_signalizer.rb, line 24
def analyse!
  overflowed_tables = []
  @models.group_by(&:table_name).each do |table, models|
    model = models.first
    next if model.abstract_class? || model.last.nil?
    pk = model.columns.select { |c| c.name == model.primary_key }.first
    max = MAX_VALUE.fetch(pk.sql_type) do |type|
      @logger.warn "Model #{model} has primary_key #{model.primary_key} with unsupported type #{type}"
      nil
    end
    next unless max
    if overflow_soon?(max, model)
      if (remaining = max - model.maximum(pk.name)) == 0
        @logger.warn("Table #{table} field #{pk.name} has overflown!")
      else
        @logger.warn("Table #{table} field #{pk.name} will overflow after #{remaining} records!")
      end
      overflowed_tables << [table, model.last.public_send(pk.name), max]
    else
      @logger.info("Table #{table} field #{pk.name} is not going to overflow in the next #{@days_count} days.")
    end
  end
  raise Overflow, overflow_message(overflowed_tables) if overflowed_tables.any?
end

Private Instance Methods

avg(model) click to toggle source
# File lib/activerecord/overflow_signalizer.rb, line 65
def avg(model)
  to = model.last.created_at
  from = to - 7 * DAY
  amount = model.where(created_at: from..to).count
  average = amount / 7
  average.zero? ? 1 : average
end
overflow_message(overflowed_tables) click to toggle source
# File lib/activerecord/overflow_signalizer.rb, line 73
def overflow_message(overflowed_tables)
  overflowed = []
  overflow_soon = []
  overflowed_tables.each do |table, current_value, max_value|
    if current_value == max_value
      overflowed << table
    else
      overflow_soon << table
    end
  end
  "Owerflowed tables: #{overflowed}. Overflow soon tables: #{overflow_soon}"
end
overflow_soon?(max, model) click to toggle source
# File lib/activerecord/overflow_signalizer.rb, line 57
def overflow_soon?(max, model)
  if model.columns.select { |c| c.name == 'created_at' }.empty?
    (max - model.last.id) / DEFAULT_AVG <= @days_count
  else
    (max - model.last.id) / avg(model) <= @days_count
  end
end
signalize(msg) click to toggle source
# File lib/activerecord/overflow_signalizer.rb, line 86
def signalize(msg)
  if @logger && @logger.respond_to?(:warn)
    @logger.warn(msg)
  end
  if @signalizer && @signalizer.respond_to?(:signalize)
    @signalizer.signalize(msg)
  end
end