Class: OneApm::Collector::SqlSampler
- Inherits:
-
Object
- Object
- OneApm::Collector::SqlSampler
- Defined in:
- lib/one_apm/collector/containers/sql_sampler.rb
Overview
This class contains the logic of recording slow SQL traces, which may represent multiple aggregated SQL queries.
A slow SQL trace consists of a collection of SQL instrumented SQL queries that all normalize to the same text. For example, the following two queries would be aggregated together into a single slow SQL trace:
SELECT * FROM table WHERE id=42
SELECT * FROM table WHERE id=1234
Each slow SQL trace keeps track of the number of times the same normalized query was seen, the min, max, and total time spent executing those queries, and an example backtrace from one of the aggregated queries.
Defined Under Namespace
Modules: Shim
Constant Summary collapse
- OA_MAX_SAMPLES =
10
Instance Attribute Summary collapse
- #disabled ⇒ Object readonly
-
#sql_traces ⇒ Object
readonly
this is for unit tests only.
Instance Method Summary collapse
- #enabled? ⇒ Boolean
- #harvest! ⇒ Object
-
#has_room? ⇒ Boolean
this should always be called under the @samples_lock.
-
#initialize ⇒ SqlSampler
constructor
A new instance of SqlSampler.
- #merge!(sql_traces) ⇒ Object
-
#notice_sql(sql, metric_name, config, duration, state = nil, &explainer) ⇒ Object
Records an SQL query, potentially creating a new slow SQL trace, or aggregating the query into an existing slow SQL trace.
-
#on_finishing_transaction(state, name, time = Time.now) ⇒ Object
This is called when we are done with the transaction.
- #on_start_transaction(state, start_time, uri = nil) ⇒ Object
-
#remove_shortest_trace ⇒ Object
this should always be called under the @samples_lock.
- #reset! ⇒ Object
-
#save_slow_sql(transaction_sql_data) ⇒ Object
this should always be called under the @samples_lock.
-
#should_add_trace?(sql_item) ⇒ Boolean
this should always be called under the @samples_lock.
-
#tl_transaction_data ⇒ Object
only used for testing.
Constructor Details
#initialize ⇒ SqlSampler
Returns a new instance of SqlSampler.
39 40 41 42 43 44 45 46 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 39 def initialize @sql_traces = {} # This lock is used to synchronize access to @sql_traces # and related variables. It can become necessary on JRuby or # any 'honest-to-god'-multithreaded system @samples_lock = Mutex.new end |
Instance Attribute Details
#disabled ⇒ Object (readonly)
32 33 34 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 32 def disabled @disabled end |
#sql_traces ⇒ Object (readonly)
this is for unit tests only
35 36 37 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 35 def sql_traces @sql_traces end |
Instance Method Details
#enabled? ⇒ Boolean
48 49 50 51 52 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 48 def enabled? OneApm::Manager.config[:'slow_sql.enabled'] && OneApm::Manager.config[:'transaction_tracer.enabled'] && OneApm::Agent::Database.should_record_sql?(:slow_sql) end |
#harvest! ⇒ Object
176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 176 def harvest! return [] unless enabled? slowest = [] @samples_lock.synchronize do slowest = @sql_traces.values @sql_traces = {} end slowest.each {|trace| trace.prepare_to_send } slowest end |
#has_room? ⇒ Boolean
this should always be called under the @samples_lock
121 122 123 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 121 def has_room? @sql_traces.size < OA_MAX_SAMPLES end |
#merge!(sql_traces) ⇒ Object
163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 163 def merge!(sql_traces) @samples_lock.synchronize do sql_traces.each do |trace| existing_trace = @sql_traces[trace.sql] if existing_trace existing_trace.aggregate_trace(trace) else @sql_traces[trace.sql] = trace end end end end |
#notice_sql(sql, metric_name, config, duration, state = nil, &explainer) ⇒ Object
Records an SQL query, potentially creating a new slow SQL trace, or aggregating the query into an existing slow SQL trace.
This method should be used only by gem authors wishing to extend the Ruby agent to instrument new database interfaces - it should generally not be called directly from application code.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 147 def notice_sql(sql, metric_name, config, duration, state=nil, &explainer) state ||= OneApm::TransactionState.tl_get data = state.sql_sampler_transaction_data return unless data if state.is_sql_recorded? if duration > OneApm::Manager.config[:'slow_sql.explain_threshold'] backtrace = caller backtrace.reject! { |t| t.include?('one_apm') } data.sql_data << SlowSql.new(OneApm::Agent::Database.capture_query(sql), metric_name, config, duration, backtrace, &explainer) end end end |
#on_finishing_transaction(state, name, time = Time.now) ⇒ Object
This is called when we are done with the transaction.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 73 def on_finishing_transaction(state, name, time=Time.now) return unless enabled? data = state.sql_sampler_transaction_data return unless data data.set_transaction_name(name) if data.sql_data.size > 0 @samples_lock.synchronize do OneApm::Manager.logger.debug "Examining #{data.sql_data.size} slow transaction sql statement(s)" save_slow_sql data end end end |
#on_start_transaction(state, start_time, uri = nil) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 54 def on_start_transaction(state, start_time, uri=nil) return unless enabled? state.sql_sampler_transaction_data = TransactionSqlData.new if state.transaction_sample_builder guid = state.transaction_sample_builder.sample.guid end if OneApm::Manager.config[:'slow_sql.enabled'] && state.sql_sampler_transaction_data state.sql_sampler_transaction_data.set_transaction_info(uri, guid) end end |
#remove_shortest_trace ⇒ Object
this should always be called under the @samples_lock
126 127 128 129 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 126 def remove_shortest_trace shortest_key, _ = @sql_traces.min_by { |(_, trace)| trace.max_call_time } @sql_traces.delete(shortest_key) end |
#reset! ⇒ Object
188 189 190 191 192 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 188 def reset! @samples_lock.synchronize do @sql_traces = {} end end |
#save_slow_sql(transaction_sql_data) ⇒ Object
this should always be called under the @samples_lock
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 89 def save_slow_sql(transaction_sql_data) path = transaction_sql_data.path uri = transaction_sql_data.uri transaction_sql_data.sql_data.each do |sql_item| normalized_sql = sql_item.normalize sql_trace = @sql_traces[normalized_sql] if sql_trace sql_trace.aggregate(sql_item, path, uri) else if has_room? sql_trace = SqlTrace.new(normalized_sql, sql_item, path, uri) elsif should_add_trace?(sql_item) remove_shortest_trace sql_trace = SqlTrace.new(normalized_sql, sql_item, path, uri) end if sql_trace @sql_traces[normalized_sql] = sql_trace end end end end |
#should_add_trace?(sql_item) ⇒ Boolean
this should always be called under the @samples_lock
114 115 116 117 118 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 114 def should_add_trace?(sql_item) @sql_traces.any? do |(_, existing_trace)| existing_trace.max_call_time < sql_item.duration end end |
#tl_transaction_data ⇒ Object
only used for testing
68 69 70 |
# File 'lib/one_apm/collector/containers/sql_sampler.rb', line 68 def tl_transaction_data # only used for testing OneApm::TransactionState.tl_get.sql_sampler_transaction_data end |