Class: ScoutRails::Store
- Inherits:
-
Object
- Object
- ScoutRails::Store
- Defined in:
- lib/scout_rails/store.rb
Overview
The store encapsolutes the logic that (1) saves instrumented data by Metric name to memory and (2) maintains a stack (just an Array) of instrumented methods that are being called. It’s accessed via ScoutRails::Agent.instance.store
.
Constant Summary collapse
- MAX_SIZE =
Limits the size of the metric hash to prevent a metric explosion.
1000
Instance Attribute Summary collapse
-
#metric_hash ⇒ Object
Returns the value of attribute metric_hash.
-
#sample ⇒ Object
Returns the value of attribute sample.
-
#stack ⇒ Object
Returns the value of attribute stack.
-
#transaction_hash ⇒ Object
Returns the value of attribute transaction_hash.
-
#transaction_sample_lock ⇒ Object
readonly
Returns the value of attribute transaction_sample_lock.
Instance Method Summary collapse
-
#aggregate_calls(metrics, parent_meta) ⇒ Object
Takes a metric_hash of calls and generates aggregates for ActiveRecord and View calls.
-
#categories(metrics) ⇒ Object
Returns the top-level category names used in the
metrics
hash. - #ignore_transaction! ⇒ Object
-
#initialize ⇒ Store
constructor
A new instance of Store.
-
#merge_data(old_data) ⇒ Object
Combines old and current data.
-
#merge_data_and_clear(old_data) ⇒ Object
Merges old and current data, clears the current in-memory metric hash, and returns the merged data.
-
#record(metric_name) ⇒ Object
Called at the start of Tracer#instrument: (1) Either finds an existing MetricStats object in the metric_hash or initialize a new one.
-
#reset_transaction! ⇒ Object
Called when the last stack item completes for the current transaction to clear for the next run.
- #stop_recording(sanity_check_item, options = {}) ⇒ Object
-
#store_metric?(stack_empty) ⇒ Boolean
TODO - Move more logic to TransactionSample.
-
#store_sample(uri, transaction_hash, parent_meta, parent_stat, options = {}) ⇒ Object
Stores the slowest transaction.
-
#track!(metric_name, call_time, options = {}) ⇒ Object
Finds or creates the metric w/the given name in the metric_hash, and updates the time.
Constructor Details
#initialize ⇒ Store
Returns a new instance of Store.
14 15 16 17 18 19 20 21 22 |
# File 'lib/scout_rails/store.rb', line 14 def initialize @metric_hash = Hash.new # Stores aggregate metrics for the current transaction. When the transaction is finished, metrics # are merged with the +metric_hash+. @transaction_hash = Hash.new @stack = Array.new # ensure background thread doesn't manipulate transaction sample while the store is. @transaction_sample_lock = Mutex.new end |
Instance Attribute Details
#metric_hash ⇒ Object
Returns the value of attribute metric_hash.
8 9 10 |
# File 'lib/scout_rails/store.rb', line 8 def metric_hash @metric_hash end |
#sample ⇒ Object
Returns the value of attribute sample.
11 12 13 |
# File 'lib/scout_rails/store.rb', line 11 def sample @sample end |
#stack ⇒ Object
Returns the value of attribute stack.
10 11 12 |
# File 'lib/scout_rails/store.rb', line 10 def stack @stack end |
#transaction_hash ⇒ Object
Returns the value of attribute transaction_hash.
9 10 11 |
# File 'lib/scout_rails/store.rb', line 9 def transaction_hash @transaction_hash end |
#transaction_sample_lock ⇒ Object (readonly)
Returns the value of attribute transaction_sample_lock.
12 13 14 |
# File 'lib/scout_rails/store.rb', line 12 def transaction_sample_lock @transaction_sample_lock end |
Instance Method Details
#aggregate_calls(metrics, parent_meta) ⇒ Object
Takes a metric_hash of calls and generates aggregates for ActiveRecord and View calls.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/scout_rails/store.rb', line 111 def aggregate_calls(metrics,) categories = categories(metrics) aggregates = {} categories.each do |cat| =ScoutRails::MetricMeta.new("#{cat}/all") .scope = .metric_name agg_stats = ScoutRails::MetricStats.new metrics.each do |,stats| if .metric_name =~ /\A#{cat}\// agg_stats.combine!(stats) end end # metrics.each aggregates[] = agg_stats unless agg_stats.call_count.zero? end # categories.each aggregates end |
#categories(metrics) ⇒ Object
Returns the top-level category names used in the metrics
hash.
99 100 101 102 103 104 105 106 107 108 |
# File 'lib/scout_rails/store.rb', line 99 def categories(metrics) cats = Set.new metrics.keys.each do || next if .scope.nil? # ignore controller if match=.metric_name.match(/\A([\w|\d]+)\//) cats << match[1] end end # metrics.each cats end |
#ignore_transaction! ⇒ Object
33 34 35 |
# File 'lib/scout_rails/store.rb', line 33 def ignore_transaction! Thread::current[:ignore_transaction] = true end |
#merge_data(old_data) ⇒ Object
Combines old and current data
152 153 154 155 156 157 158 159 160 161 |
# File 'lib/scout_rails/store.rb', line 152 def merge_data(old_data) old_data.each do |,old_stats| if stats = metric_hash[] metric_hash[] = stats.combine!(old_stats) elsif metric_hash.size < MAX_SIZE metric_hash[] = old_stats end end metric_hash end |
#merge_data_and_clear(old_data) ⇒ Object
Merges old and current data, clears the current in-memory metric hash, and returns the merged data
165 166 167 168 169 |
# File 'lib/scout_rails/store.rb', line 165 def merge_data_and_clear(old_data) merged = merge_data(old_data) self.metric_hash = {} merged end |
#record(metric_name) ⇒ Object
Called at the start of Tracer#instrument: (1) Either finds an existing MetricStats object in the metric_hash or initialize a new one. An existing MetricStats object is present if this metric_name
has already been instrumented. (2) Adds a StackItem to the stack. This StackItem is returned and later used to validate the item popped off the stack when an instrumented code block completes.
42 43 44 45 46 |
# File 'lib/scout_rails/store.rb', line 42 def record(metric_name) item = ScoutRails::StackItem.new(metric_name) stack << item item end |
#reset_transaction! ⇒ Object
Called when the last stack item completes for the current transaction to clear for the next run.
26 27 28 29 30 31 |
# File 'lib/scout_rails/store.rb', line 26 def reset_transaction! Thread::current[:ignore_transaction] = nil Thread::current[:scout_scope_name] = nil @transaction_hash = Hash.new @stack = Array.new end |
#stop_recording(sanity_check_item, options = {}) ⇒ Object
48 49 50 51 52 53 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 83 84 85 86 87 88 |
# File 'lib/scout_rails/store.rb', line 48 def stop_recording(sanity_check_item, ={}) item = stack.pop stack_empty = stack.empty? # if ignoring the transaction, the item is popped but nothing happens. if Thread::current[:ignore_transaction] return end # unbalanced stack check - unreproducable cases have seen this occur. when it does, sets a Thread variable # so we ignore further recordings. +Store#reset_transaction!+ resets this. if item != sanity_check_item ScoutRails::Agent.instance.logger.warn "Scope [#{Thread::current[:scout_scope_name]}] Popped off stack: #{item.inspect} Expected: #{sanity_check_item.inspect}. Aborting." ignore_transaction! return end duration = Time.now - item.start_time if last=stack.last last.children_time += duration end = ScoutRails::MetricMeta.new(item.metric_name, :desc => [:desc]) .scope = nil if stack_empty # add backtrace for slow calls ... how is exclusive time handled? if duration > ScoutRails::TransactionSample::BACKTRACE_THRESHOLD and !stack_empty .extra = {:backtrace => ScoutRails::TransactionSample.backtrace_parser(caller)} end stat = transaction_hash[] || ScoutRails::MetricStats.new(!stack_empty) stat.update!(duration,duration-item.children_time) transaction_hash[] = stat if store_metric?(stack_empty) # Uses controllers as the entry point for a transaction. Otherwise, stats are ignored. if stack_empty and .metric_name.match(/\AController\//) aggs=aggregate_calls(transaction_hash.dup,) store_sample([:uri],transaction_hash.dup.merge(aggs),,stat) # deep duplicate duplicate = aggs.dup duplicate.each_pair do |k,v| duplicate[k.dup] = v.dup end merge_data(duplicate.merge({.dup => stat.dup})) # aggregrates + controller end end |
#store_metric?(stack_empty) ⇒ Boolean
TODO - Move more logic to TransactionSample
Limits the size of the transaction hash to prevent a large transactions. The final item on the stack is allowed to be stored regardless of hash size to wrapup the transaction sample w/the parent metric.
94 95 96 |
# File 'lib/scout_rails/store.rb', line 94 def store_metric?(stack_empty) transaction_hash.size < ScoutRails::TransactionSample::MAX_SIZE or stack_empty end |
#store_sample(uri, transaction_hash, parent_meta, parent_stat, options = {}) ⇒ Object
Stores the slowest transaction. This will be sent to the server.
129 130 131 132 133 134 135 |
# File 'lib/scout_rails/store.rb', line 129 def store_sample(uri,transaction_hash,,parent_stat, = {}) @transaction_sample_lock.synchronize do if parent_stat.total_call_time >= 2 and (@sample.nil? or (@sample and parent_stat.total_call_time > @sample.total_call_time)) @sample = ScoutRails::TransactionSample.new(uri,.metric_name,parent_stat.total_call_time,transaction_hash.dup) end end end |
#track!(metric_name, call_time, options = {}) ⇒ Object
Finds or creates the metric w/the given name in the metric_hash, and updates the time. Primarily used to record sampled metrics. For instrumented methods, #record and #stop_recording are used.
Options: :scope => If provided, overrides the default scope. :exclusive_time => Sets the exclusive time for the method. If not provided, uses call_time
.
143 144 145 146 147 148 149 |
# File 'lib/scout_rails/store.rb', line 143 def track!(metric_name,call_time, = {}) = ScoutRails::MetricMeta.new(metric_name) .scope = [:scope] if .has_key?(:scope) stat = metric_hash[] || ScoutRails::MetricStats.new stat.update!(call_time,[:exclusive_time] || call_time) metric_hash[] = stat end |