app/models/concerns/counter/verifyable.rb in counterwise-0.1.1 vs app/models/concerns/counter/verifyable.rb in counterwise-0.1.2

- old
+ new

@@ -1,14 +1,67 @@ module Counter::Verifyable extend ActiveSupport::Concern def correct? - count_by_sql == value + # We can't verify these values + return true if definition.global? + + old_value, new_value = verify + old_value == new_value end def correct! - requires_recalculation = !correct? - recalc! if requires_recalculation + # We can't verify these values + return true if definition.global? + old_value, new_value = verify + + requires_recalculation = old_value != new_value + update! value: new_value if requires_recalculation + !requires_recalculation + end + + def verify + if definition.calculated? + [calculate, value] + else + [count_by_sql, value] + end + end + + class_methods do + # on_error: raise, log, correct + # Returns the number of incorrect counters + def sample_and_verify scope: -> { all }, samples: 1000, verbose: true, on_error: :raise + incorrect_counters = 0 + + counter_range = Counter::Value.minimum(:id)..Counter::Value.maximum(:id) + + samples.times do + random_id = rand(counter_range) + counter = Counter::Value.merge(scope).where("id >= ?", random_id).limit(1).first + next if counter.nil? + + if counter.definition.global? || counter.definition.calculated? + puts "➡️ Skipping counter #{counter.name} (#{counter.id})" if verbose + next + end + + if counter.correct? + puts "✅ Counter #{counter.id} is correct" if verbose + else + incorrect_counters += 1 + message = "❌ counter #{counter.name} (#{counter.id}) for #{counter.parent_type}##{counter.parent_id} has incorrect counter value. Expected #{counter.value} but got #{counter.count_by_sql}" + + case on_error + when :raise then raise Counter::Error.new(message) + when :log then Rails.logger.error message + when :correct then counter.correct! + end + end + sleep 0.1 + end + incorrect_counters + end end end