Module: Metrics::ClassMethods
- Defined in:
- lib/has_metrics/metrics.rb
Instance Method Summary collapse
- #aggregate_metrics ⇒ Object
- #collect_metrics(warmup) ⇒ Object
- #crank_aggregate_metrics! ⇒ Object
- #crank_detected_aggregate_metrics!(detected_aggregate_metrics) ⇒ Object
- #crank_singular_metrics!(args, singular_metrics) ⇒ Object
- #define_single_method(name, options) ⇒ Object
- #has_aggregate_metric(name, sql) ⇒ Object
- #has_metric(name, options = {}, &block) ⇒ Object
- #metrics ⇒ Object
-
#metrics_class ⇒ Object
CLASS METHODS ADDED.
- #metrics_column_type(column) ⇒ Object
- #single_only_metrics ⇒ Object
- #update_all_metrics!(*args) ⇒ Object
Instance Method Details
#aggregate_metrics ⇒ Object
92 93 94 |
# File 'lib/has_metrics/metrics.rb', line 92 def aggregate_metrics metrics.select{ |metric, | .has_key?(:aggregate) } end |
#collect_metrics(warmup) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/has_metrics/metrics.rb', line 134 def collect_metrics(warmup) detected_aggregate_metrics = {} metrics.select{|m,o| o[:infer_aggregate] }.each do |metric_name, | next if [:aggregate] existing_logger = ActiveRecord::Base.logger sql_capturer = ActiveRecord::Base.logger = SqlCapturer.new(existing_logger) warmup.instance_exec(&[:single]) ActiveRecord::Base.logger = existing_logger unless sql_capturer.query.count != 1 subquery = sql_capturer.query.first.gsub(warmup.id.to_s, "#{metrics_class.table_name}.id") unless subquery == %Q{SELECT "#{metrics_class.table_name}".* FROM "#{metrics_class.table_name}" WHERE "#{metrics_class.table_name}"."id" = #{metrics_class.table_name}.id LIMIT 1} update_sql = %Q{ UPDATE #{metrics_class.table_name} SET #{metric_name} = (#{subquery}), updated__#{metric_name}__at = '#{Time.current.to_s(:db)}'; } detected_aggregate_metrics[metric_name] = update_sql end end end return detected_aggregate_metrics, (metrics.keys - aggregate_metrics.keys - detected_aggregate_metrics.keys) end |
#crank_aggregate_metrics! ⇒ Object
190 191 192 193 194 195 |
# File 'lib/has_metrics/metrics.rb', line 190 def crank_aggregate_metrics! aggregate_metrics.each do |metric_name, | ActiveRecord::Base.connection.execute [:aggregate] metrics_class.update_all "updated__#{metric_name}__at" => Time.current end end |
#crank_detected_aggregate_metrics!(detected_aggregate_metrics) ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/has_metrics/metrics.rb', line 158 def crank_detected_aggregate_metrics!(detected_aggregate_metrics) bad_guesses = [] detected_aggregate_metrics.each do |metric_name, update_sql| begin ActiveRecord::Base.connection.execute update_sql rescue bad_guesses << metric_name end end bad_guesses end |
#crank_singular_metrics!(args, singular_metrics) ⇒ Object
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/has_metrics/metrics.rb', line 170 def crank_singular_metrics!(args, singular_metrics) unless ENV['RAILS_ENV'] == 'test' puts "Slow Metrics Found :: #{singular_metrics.count} \n" puts " -- -- -- -- \n" puts "Go implement their aggregate methods in #{self} to speed this up.\n" puts singular_metrics.join(', ') puts "\n ≈ ≈ ≈ ≈ ≈ ≈ ≈ " end find_in_batches do |batch| metrics_class.transaction do batch.each do |record| singular_metrics.each do |singular_metric| record.send(singular_metric, *args) end end end end end |
#define_single_method(name, options) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/has_metrics/metrics.rb', line 54 def define_single_method(name, ) define_method name do |*args| previous_result = metrics.attributes[name.to_s] unless [:every] == :always datestamp_column = "updated__#{name}__at" force = [:force, true].include?(args[0]) if !force && (.has_key?(:aggregate) || [:infer_aggregate]) previous_result else result = instance_exec(&[:single]) result = nil if result.is_a?(Float) && !result.finite? begin metrics.send "#{name}=", result metrics.send "#{datestamp_column}=", Time.current rescue NoMethodError => e raise e unless e.name == "#{name}=".to_sym # This happens if the migrations haven't run yet for this metric. We should still calculate & return the metric. end unless changed? metrics.save end result end end if defined?(::NewRelic) ::NewRelic::Agent.logger.debug "Installing instrumentation for #{name}" add_transaction_tracer name, category: :task end end |
#has_aggregate_metric(name, sql) ⇒ Object
50 51 52 |
# File 'lib/has_metrics/metrics.rb', line 50 def has_aggregate_metric(name, sql) has_metric name, {}.merge(aggregate: sql) end |
#has_metric(name, options = {}, &block) ⇒ Object
39 40 41 42 43 44 45 46 47 48 |
# File 'lib/has_metrics/metrics.rb', line 39 def has_metric name, ={}, &block .merge!(single: block) if block define_single_method(name, ) if [:single] metrics[name.to_sym] ||= {} metrics[name.to_sym].merge!() metrics_class.class_eval do attr_accessible(name, "updated__#{name}__at") end end |
#metrics ⇒ Object
84 85 86 |
# File 'lib/has_metrics/metrics.rb', line 84 def metrics @metrics ||= {} end |
#metrics_class ⇒ Object
CLASS METHODS ADDED
35 36 37 |
# File 'lib/has_metrics/metrics.rb', line 35 def metrics_class @metrics_class end |
#metrics_column_type(column) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/has_metrics/metrics.rb', line 96 def metrics_column_type(column) case when (metric = metrics.select { |metric, | metric == column.to_sym && [:type] }).any? metric.values.first[:type] when (column.to_s =~ /^by_(.+)$/) && respond_to?(:segment_categories) && segment_categories.include?($1.to_sym) # TODO: carve out segementation functionality into this gem :string when (column.to_s =~ /_at$/) :datetime else :integer end end |
#single_only_metrics ⇒ Object
88 89 90 |
# File 'lib/has_metrics/metrics.rb', line 88 def single_only_metrics metrics.select{ |metric, | !.has_key?(:aggregate) } end |
#update_all_metrics!(*args) ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/has_metrics/metrics.rb', line 109 def update_all_metrics!(*args) metrics_class.migrate! return unless warmup = first where("not exists(select id from #{metrics_class.table_name} where #{metrics_class.table_name}.id = #{table_name}.id)").each do |resource| resource.metrics.save! end detected_aggregate_metrics, singular_metrics = collect_metrics(warmup) bad_guesses = crank_detected_aggregate_metrics!(detected_aggregate_metrics) puts "#{bad_guesses.count} bad guesses found, scheduling as singular metrics." if bad_guesses.any? singular_metrics = singular_metrics | bad_guesses if singular_metrics.any? crank_singular_metrics!(args, singular_metrics) end crank_aggregate_metrics! metrics end |