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



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.

Parameters:

  • start_time (Float)

    the time the transaction started

  • end_time (Float)

    the time the transaction ended



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.

Returns:

  • (Integer, nil)


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

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



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.



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

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



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.

Parameters:

  • connection_class (Class)

    the connection adapter class to extend



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

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.

Returns:

  • (Float, nil)

    the monotonic time when the first transaction began,



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

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)


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

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,



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

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)


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

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)


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

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)


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

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.



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

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)


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

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)


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

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.



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

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