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.

Parameters:

  • row_count (Integer)

    the number of rows returned by the query

  • elapsed_time (Float)

    the time spent executing the query



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

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.

Parameters:

  • start_time (Float)

    the time the transaction started

  • end_time (Float)

    the time the transaction ended



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

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.

Returns:

  • (Integer, nil)


129
130
131
132
# File 'lib/active_record_query_counter.rb', line 129

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.

Returns:

  • (Object)

    the result of the block



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

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.



221
222
223
# File 'lib/active_record_query_counter.rb', line 221

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.

Returns:

  • (Object)

    the return value of the block



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

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.

Parameters:

  • connection_class (Class)

    the connection adapter class to extend



235
236
237
238
239
240
241
242
243
244
245
# File 'lib/active_record_query_counter.rb', line 235

def enable!(connection_class)
  ConnectionAdapterExtension.inject(connection_class)
  TransactionManagerExtension.inject(ActiveRecord::ConnectionAdapters::TransactionManager)

  @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.

Returns:

  • (Float, nil)

    the monotonic time when the first transaction began,



174
175
176
177
# File 'lib/active_record_query_counter.rb', line 174

def first_transaction_start_time
  counter = current_counter
  counter.first_transaction_start_time if counter.is_a?(Counter)
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.

Returns:

  • (Hash, nil)


202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/active_record_query_counter.rb', line 202

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
    }
  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.

Returns:

  • (Float, nil)

    the monotonic time when the last transaction ended,



183
184
185
186
# File 'lib/active_record_query_counter.rb', line 183

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.

Returns:

  • (Integer, nil)


119
120
121
122
# File 'lib/active_record_query_counter.rb', line 119

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.

Returns:

  • (Float, nil)


147
148
149
150
# File 'lib/active_record_query_counter.rb', line 147

def query_time
  counter = current_counter
  counter.query_time 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.

Returns:

  • (Integer, nil)


138
139
140
141
# File 'lib/active_record_query_counter.rb', line 138

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.



227
228
229
# File 'lib/active_record_query_counter.rb', line 227

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.

Returns:

  • (Integer, nil)


156
157
158
159
# File 'lib/active_record_query_counter.rb', line 156

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.

Returns:

  • (Float, nil)


165
166
167
168
# File 'lib/active_record_query_counter.rb', line 165

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.



192
193
194
195
# File 'lib/active_record_query_counter.rb', line 192

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