Module: ActiveRecordQueryCounter

Defined in:
lib/active_record_query_counter.rb,
lib/active_record_query_counter/counter.rb,
lib/active_record_query_counter/version.rb,
lib/active_record_query_counter/thresholds.rb,
lib/active_record_query_counter/rack_middleware.rb,
lib/active_record_query_counter/transaction_info.rb,
lib/active_record_query_counter/sidekiq_middleware.rb,
lib/active_record_query_counter/connection_adapter_extension.rb,
lib/active_record_query_counter/transaction_manager_extension.rb

Overview

Everything you need to count ActiveRecord queries and row counts within a block.

Examples:


ActiveRecordQueryCounter.count_queries do
  yield
  puts ActiveRecordQueryCounter.query_count
  puts ActiveRecordQueryCounter.row_count
end

Defined Under Namespace

Modules: ConnectionAdapterExtension, TransactionManagerExtension Classes: Counter, RackMiddleware, SidekiqMiddleware, Thresholds, TransactionInfo

Constant Summary collapse

VERSION =
File.read(File.join(__dir__, "..", "..", "VERSION")).strip

Class Method Summary collapse

Class Method Details

.add_query(sql, name, binds, row_count, start_time, end_time) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Increment the query counters.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/active_record_query_counter.rb', line 72

def add_query(sql, name, binds, row_count, start_time, end_time)
  return if IGNORED_STATEMENTS.include?(name)

  counter = current_counter
  return unless counter.is_a?(Counter)

  elapsed_time = end_time - start_time
  counter.query_count += 1
  counter.row_count += row_count
  counter.query_time += elapsed_time

  trace = nil
  query_time_threshold = (counter.thresholds.query_time || -1)
  if query_time_threshold >= 0 && elapsed_time >= query_time_threshold
    trace = backtrace
    send_notification("query_time", start_time, end_time, sql: sql, binds: binds, row_count: row_count, trace: trace)
  end

  row_count_threshold = (counter.thresholds.row_count || -1)
  if row_count_threshold >= 0 && row_count >= row_count_threshold
    trace ||= backtrace
    send_notification("row_count", start_time, end_time, sql: sql, binds: binds, row_count: row_count, trace: trace)
  end
end

.add_transaction(start_time, end_time) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Increment the transaction counters.



103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/active_record_query_counter.rb', line 103

def add_transaction(start_time, end_time)
  counter = current_counter
  return unless counter.is_a?(Counter)

  trace = backtrace
  counter.add_transaction(trace: trace, start_time: start_time, end_time: end_time)

  transaction_time_threshold = (counter.thresholds.transaction_time || -1)
  if transaction_time_threshold >= 0 && end_time - start_time >= transaction_time_threshold
    send_notification("transaction_time", start_time, end_time, trace: backtrace)
  end
end

.cached_query_countInteger?

Return the number of queries that hit the query cache and were not sent to the database that have been counted within the current block. Returns nil if not inside a block where queries are being counted.



141
142
143
144
# File 'lib/active_record_query_counter.rb', line 141

def cached_query_count
  counter = current_counter
  counter.cached_query_count if counter.is_a?(Counter)
end

.count_queriesObject

Enable query counting within a block.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/active_record_query_counter.rb', line 30

def count_queries
  save_counter = current_counter
  begin
    counter = Counter.new
    self.current_counter = counter

    retval = yield

    transaction_count = counter.transaction_count
    if transaction_count > 0
      transaction_threshold = (counter.thresholds.transaction_count || -1)
      if transaction_threshold >= 0 && transaction_count >= transaction_threshold
        send_notification("transaction_count", counter.first_transaction_start_time, counter.last_transaction_end_time, transactions: counter.transactions)
      end
    end

    retval
  ensure
    self.current_counter = save_counter
  end
end

.default_thresholdsActiveRecordQueryCounter::Thresholds

The global notification thresholds for sending notifications. The values set in these thresholds are used as the default values.



243
244
245
# File 'lib/active_record_query_counter.rb', line 243

def default_thresholds
  @default_thresholds ||= Thresholds.new
end

.disable(&block) ⇒ Object

Disable query counting in a block. Any queries or transactions inside the block will not be counted.



56
57
58
59
60
61
62
63
64
# File 'lib/active_record_query_counter.rb', line 56

def disable(&block)
  counter = current_counter
  begin
    self.current_counter = nil
    yield
  ensure
    self.current_counter = counter
  end
end

.enable!(connection_class) ⇒ void

This method returns an undefined value.

Enable the query counting behavior on a connection adapter class.



257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/active_record_query_counter.rb', line 257

def enable!(connection_class)
  ActiveSupport.on_load(:active_record) do
    ConnectionAdapterExtension.inject(connection_class)
    TransactionManagerExtension.inject(ActiveRecord::ConnectionAdapters::TransactionManager)
  end

  @cache_subscription ||= ActiveSupport::Notifications.subscribe("sql.active_record") do |_name, _start_time, _end_time, _id, payload|
    if payload[:cached]
      counter = current_counter
      counter.cached_query_count += 1 if counter
    end
  end
end

.first_transaction_start_timeFloat?

Return the time when the first transaction began within the current block. Returns nil if not inside a block where queries are being counted or there are no transactions.



186
187
188
189
# File 'lib/active_record_query_counter.rb', line 186

def first_transaction_start_time
  counter = current_counter
  counter.first_transaction_start_time if counter.is_a?(Counter)
end

.increment_rollbacksInteger?

Return the number of rollbacks that have been counted within the current block. Returns nil if not inside a block where queries are being counted.



120
121
122
123
124
125
# File 'lib/active_record_query_counter.rb', line 120

def increment_rollbacks
  counter = current_counter
  return unless counter.is_a?(Counter)

  counter.rollback_count += 1
end

.infoHash?

Return the query info as a hash with keys :query_count, :row_count, :query_time :transaction_count, and :transaction_type or nil if not inside a block where queries are being counted.



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/active_record_query_counter.rb', line 223

def info
  counter = current_counter
  if counter.is_a?(Counter)
    {
      query_count: counter.query_count,
      row_count: counter.row_count,
      query_time: counter.query_time,
      cached_query_count: counter.cached_query_count,
      cache_hit_rate: counter.cache_hit_rate,
      transaction_count: counter.transaction_count,
      transaction_time: counter.transaction_time,
      rollback_count: counter.rollback_count
    }
  end
end

.last_transaction_end_timeFloat?

Return the time when the last transaction ended within the current block. Returns nil if not inside a block where queries are being counted or there are no transactions.



195
196
197
198
# File 'lib/active_record_query_counter.rb', line 195

def last_transaction_end_time
  counter = current_counter
  counter.transactions.last&.end_time if counter.is_a?(Counter)
end

.query_countInteger?

Return the number of queries that have been counted within the current block. Returns nil if not inside a block where queries are being counted.



131
132
133
134
# File 'lib/active_record_query_counter.rb', line 131

def query_count
  counter = current_counter
  counter.query_count if counter.is_a?(Counter)
end

.query_timeFloat?

Return the total time spent executing queries within the current block. Returns nil if not inside a block where queries are being counted.



159
160
161
162
# File 'lib/active_record_query_counter.rb', line 159

def query_time
  counter = current_counter
  counter.query_time if counter.is_a?(Counter)
end

.rollback_countInteger?

Return the number of transactions that have rolled back within the current block. Returns nil if not inside a block where queries are being counted.



213
214
215
216
# File 'lib/active_record_query_counter.rb', line 213

def rollback_count
  counter = current_counter
  counter.rollback_count if counter.is_a?(Counter)
end

.row_countInteger?

Return the number of rows that have been counted within the current block. Returns nil if not inside a block where queries are being counted.



150
151
152
153
# File 'lib/active_record_query_counter.rb', line 150

def row_count
  counter = current_counter
  counter.row_count if counter.is_a?(Counter)
end

.thresholdsObject

Get the current local notification thresholds. These thresholds are only used within the current ‘count_queries` block.



249
250
251
# File 'lib/active_record_query_counter.rb', line 249

def thresholds
  current_counter&.thresholds || default_thresholds.dup
end

.transaction_countInteger?

Return the number of transactions that have been counted within the current block. Returns nil if not inside a block where queries are being counted.



168
169
170
171
# File 'lib/active_record_query_counter.rb', line 168

def transaction_count
  counter = current_counter
  counter.transaction_count if counter.is_a?(Counter)
end

.transaction_timeFloat?

Return the total time spent in transactions that have been counted within the current block. Returns nil if not inside a block where queries are being counted.



177
178
179
180
# File 'lib/active_record_query_counter.rb', line 177

def transaction_time
  counter = current_counter
  counter.transaction_time if counter.is_a?(Counter)
end

.transactionsArray<ActiveRecordQueryCounter::TransactionInfo>?

Return an array of transaction information for any transactions that have been counted within the current block. Returns nil if not inside a block where queries are being counted.



204
205
206
207
# File 'lib/active_record_query_counter.rb', line 204

def transactions
  counter = current_counter
  counter.transactions if counter.is_a?(Counter)
end