active_record_query_counter 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +76 -32
- data/VERSION +1 -1
- data/active_record_query_counter.gemspec +7 -1
- data/lib/active_record_query_counter/counter.rb +2 -1
- data/lib/active_record_query_counter/thresholds.rb +1 -1
- data/lib/active_record_query_counter/transaction_manager_extension.rb +1 -0
- data/lib/active_record_query_counter.rb +30 -6
- metadata +10 -6
- /data/{MIT_LICENSE → MIT_LICENSE.txt} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b460515835154824cb80fec7db1e62dc4f4f983f8779fc1b80bec3c728646a4
|
4
|
+
data.tar.gz: 3d26302dd73d2ae010f4732ca4711a7dd1daa4ca0cca55e5ccd50349e89b282c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1af45ed8c0659df4fcbf665621576b07380565a1289458e868b7cb33825c2d055e7b489eb1e490c31a566157a01f328eedd2771f9f3f5bffdece2b357445a01
|
7
|
+
data.tar.gz: ba21e07f668e1c3f97c61ce6f48fd10ef2d019afdd7d9d579e3487c710d58e8b44a9b1c5042572bc74be55a907c506d6385947dbe85a10b14b57e2b7b94037b7
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## 2.3.0
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Added count of rollbacks from transactions
|
12
|
+
|
13
|
+
## 2.2.1
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
|
17
|
+
- Classes not required to run the gem are now lazy loaded.
|
18
|
+
|
7
19
|
## 2.2.0
|
8
20
|
|
9
21
|
### Added
|
data/README.md
CHANGED
@@ -1,37 +1,47 @@
|
|
1
1
|
# ActiveRecordQueryCounter
|
2
2
|
|
3
|
-
](https://github.com/bdurand/active_record_query_counter/actions/workflows/continuous_integration.yml)
|
4
4
|
[](https://github.com/testdouble/standard)
|
5
|
+
[](https://badge.fury.io/rb/active_record_query_counter)
|
5
6
|
|
6
|
-
|
7
|
+
**ActiveRecordQueryCounter** is a ruby gem that provides detailed insights into how your code interacts with the database by hooking into ActiveRecord.
|
7
8
|
|
8
|
-
|
9
|
+
It measures database usage within a block of code, including:
|
9
10
|
|
10
|
-
-
|
11
|
-
-
|
12
|
-
-
|
13
|
-
-
|
14
|
-
-
|
11
|
+
- The number of queries executed
|
12
|
+
- The number of rows returned
|
13
|
+
- The total time spent on queries
|
14
|
+
- The number of transactions used
|
15
|
+
- The total time spent inside transactions
|
16
|
+
- The number of transactions that were rolled back
|
15
17
|
|
16
|
-
|
18
|
+
This gem is designed to help you:
|
19
|
+
|
20
|
+
- Identify "hot spots" in your code that generate excessive or slow queries.
|
21
|
+
- Spot queries returning unexpectedly large result sets.
|
22
|
+
- Detect areas where transactions are underutilized, especially when performing multiple database updates.
|
17
23
|
|
18
24
|
## Usage
|
19
25
|
|
20
|
-
|
26
|
+
### Enabling The Gem
|
27
|
+
|
28
|
+
To use **ActiveRecordQueryCounter**, you first need to enable it on your database connection adapter. Add the following to an initializer:
|
21
29
|
|
22
|
-
|
30
|
+
For **PostgreSQL**:
|
23
31
|
|
24
32
|
```ruby
|
25
33
|
ActiveRecordQueryCounter.enable!(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
|
26
34
|
```
|
27
35
|
|
28
|
-
MySQL
|
36
|
+
For **MySQL**:
|
29
37
|
|
30
38
|
```ruby
|
31
39
|
ActiveRecordQueryCounter.enable!(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
|
32
40
|
```
|
33
41
|
|
34
|
-
|
42
|
+
### Counting Queries
|
43
|
+
|
44
|
+
To measure database activity, wrap the code you want to monitor inside a `count_queries` block:
|
35
45
|
|
36
46
|
```ruby
|
37
47
|
ActiveRecordQueryCounter.count_queries do
|
@@ -41,18 +51,25 @@ ActiveRecordQueryCounter.count_queries do
|
|
41
51
|
puts "Query Time: #{ActiveRecordQueryCounter.query_time}"
|
42
52
|
puts "Transactions: #{ActiveRecordQueryCounter.transaction_count}"
|
43
53
|
puts "Transaction Time: #{ActiveRecordQueryCounter.transaction_time}"
|
54
|
+
puts "Rollbacks: #{ActiveRecordQueryCounter.rollback_count}"
|
44
55
|
end
|
45
56
|
```
|
46
57
|
|
47
|
-
|
58
|
+
### Middleware Integration
|
59
|
+
|
60
|
+
For **Rails** and **Sidekiq**, middleware is included to enable query counting in web requests and workers.
|
61
|
+
|
62
|
+
Add the following to an initializer:
|
48
63
|
|
49
64
|
```ruby
|
50
65
|
ActiveSupport.on_load(:active_record) do
|
51
66
|
ActiveRecordQueryCounter.enable!(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
|
52
67
|
end
|
53
68
|
|
69
|
+
# Enable Rack Middleware
|
54
70
|
Rails.application.config.middleware.use(ActiveRecordQueryCounter::RackMiddleware)
|
55
71
|
|
72
|
+
# Enable Sidekiq Middleware
|
56
73
|
Sidekiq.configure_server do |config|
|
57
74
|
config.server_middleware do |chain|
|
58
75
|
chain.add ActiveRecordQueryCounter::SidekiqMiddleware
|
@@ -60,13 +77,15 @@ Sidekiq.configure_server do |config|
|
|
60
77
|
end
|
61
78
|
```
|
62
79
|
|
63
|
-
|
80
|
+
### Disabling Query Counting
|
81
|
+
|
82
|
+
You can temporarily disable query counting within a block using `disable`:
|
64
83
|
|
65
84
|
```ruby
|
66
85
|
ActiveRecordQueryCounter.count_queries do
|
67
86
|
do_something
|
68
87
|
ActiveRecordQueryCounter.disable do
|
69
|
-
# Queries will not be counted
|
88
|
+
# Queries in this block will not be counted.
|
70
89
|
do_something_else
|
71
90
|
end
|
72
91
|
end
|
@@ -74,43 +93,46 @@ end
|
|
74
93
|
|
75
94
|
### Notifications
|
76
95
|
|
77
|
-
|
96
|
+
**ActiveRecordQueryCounter** supports ActiveSupport notifications when certain query thresholds are exceeded.
|
97
|
+
|
98
|
+
#### Available Notifications
|
99
|
+
|
100
|
+
##### 1. active_record_query_counter.query_time notification
|
78
101
|
|
79
|
-
|
102
|
+
Triggered when a query exceeds the query_time threshold with the payload:
|
80
103
|
|
81
|
-
This notification is triggered when a query takes longer than the `query_time` threshold. The payload contains the following keys:
|
82
104
|
|
83
105
|
- `:sql` - The SQL statement that was executed.
|
84
106
|
- `:binds` - The bind parameters that were used.
|
85
107
|
- `:row_count` - The number of rows returned.
|
86
108
|
- `:trace` - The stack trace of where the query was executed.
|
87
109
|
|
88
|
-
|
110
|
+
##### 2. active_record_query_counter.row_count notification
|
89
111
|
|
90
|
-
|
112
|
+
Triggered when a query exceeds the row_count threshold with the payload:
|
91
113
|
|
92
114
|
- `:sql` - The SQL statement that was executed.
|
93
115
|
- `:binds` - The bind parameters that were used.
|
94
116
|
- `:row_count` - The number of rows returned.
|
95
117
|
- `:trace` - The stack trace of where the query was executed.
|
96
118
|
|
97
|
-
|
119
|
+
##### 3. active_record_query_counter.transaction_time notification
|
98
120
|
|
99
|
-
|
121
|
+
Triggered when a transaction exceeds the transaction_time threshold with the payload:
|
100
122
|
|
101
123
|
- `:trace` - The stack trace of where the transaction was completed.
|
102
124
|
|
103
|
-
|
125
|
+
##### 4. active_record_query_counter.transaction_count notification
|
104
126
|
|
105
|
-
|
127
|
+
Triggered when transactions exceed the transaction_count threshold with the payload:
|
106
128
|
|
107
129
|
- `:transactions` - An array of `ActiveRecordQueryCounter::TransactionInfo` objects.
|
108
130
|
|
109
131
|
The duration of the notification event is the time between when the first transaction was started and the last transaction was completed.
|
110
132
|
|
111
|
-
#### Thresholds
|
133
|
+
#### Setting Thresholds
|
112
134
|
|
113
|
-
|
135
|
+
Thresholds can be configured **globally** in an initializer:
|
114
136
|
|
115
137
|
```ruby
|
116
138
|
ActiveRecordQueryCounter.default_thresholds.set(
|
@@ -121,7 +143,7 @@ ActiveRecordQueryCounter.default_thresholds.set(
|
|
121
143
|
)
|
122
144
|
```
|
123
145
|
|
124
|
-
|
146
|
+
Or locally within a block:
|
125
147
|
|
126
148
|
```ruby
|
127
149
|
ActiveRecordQueryCounter.count_queries do
|
@@ -134,7 +156,8 @@ ActiveRecordQueryCounter.count_queries do
|
|
134
156
|
end
|
135
157
|
```
|
136
158
|
|
137
|
-
|
159
|
+
#### Sidekiq Worker Thresholds
|
160
|
+
Thresholds for individual Sidekiq workers can be set using `sidekiq_options`:
|
138
161
|
|
139
162
|
```ruby
|
140
163
|
class MyWorker
|
@@ -150,7 +173,6 @@ class MyWorker
|
|
150
173
|
}
|
151
174
|
}
|
152
175
|
)
|
153
|
-
# You can disable thresholds for the worker by setting `thresholds: false`.
|
154
176
|
|
155
177
|
def perform
|
156
178
|
do_something
|
@@ -158,7 +180,11 @@ class MyWorker
|
|
158
180
|
end
|
159
181
|
```
|
160
182
|
|
161
|
-
|
183
|
+
To disable thresholds for a worker, set `thresholds: false`.
|
184
|
+
|
185
|
+
#### Rack Middleware Thresholds
|
186
|
+
|
187
|
+
You can configure separate thresholds for the Rack middleware:
|
162
188
|
|
163
189
|
```ruby
|
164
190
|
Rails.application.config.middleware.use(ActiveRecordQueryCounter::RackMiddleware, thresholds: {
|
@@ -169,7 +195,7 @@ Rails.application.config.middleware.use(ActiveRecordQueryCounter::RackMiddleware
|
|
169
195
|
})
|
170
196
|
```
|
171
197
|
|
172
|
-
#### Example
|
198
|
+
#### Example: Subscribing to Notifications
|
173
199
|
|
174
200
|
```ruby
|
175
201
|
ActiveRecordQueryCounter.default_thresholds.query_time = 1.0
|
@@ -204,6 +230,24 @@ ActiveSupport::Notifications.subscribe('active_record_query_counter.transaction_
|
|
204
230
|
end
|
205
231
|
```
|
206
232
|
|
233
|
+
## Installation
|
234
|
+
|
235
|
+
Add this line to your application's Gemfile:
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
gem 'active_record_query_counter'
|
239
|
+
```
|
240
|
+
|
241
|
+
And then execute:
|
242
|
+
```bash
|
243
|
+
$ bundle
|
244
|
+
```
|
245
|
+
|
246
|
+
Or install it yourself as:
|
247
|
+
```bash
|
248
|
+
$ gem install active_record_query_counter
|
249
|
+
```
|
250
|
+
|
207
251
|
## Contributing
|
208
252
|
|
209
253
|
Open a pull request on GitHub.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.3.0
|
@@ -4,10 +4,16 @@ Gem::Specification.new do |spec|
|
|
4
4
|
spec.authors = ["Brian Durand"]
|
5
5
|
spec.email = ["bbdurand@gmail.com"]
|
6
6
|
|
7
|
-
spec.summary = "
|
7
|
+
spec.summary = "Provides detailed insights into how your code interacts with the database by hooking into ActiveRecord."
|
8
8
|
spec.homepage = "https://github.com/bdurand/active_record_query_counter"
|
9
9
|
spec.license = "MIT"
|
10
10
|
|
11
|
+
spec.metadata = {
|
12
|
+
"homepage_uri" => spec.homepage,
|
13
|
+
"source_code_uri" => spec.homepage,
|
14
|
+
"changelog_uri" => "#{spec.homepage}/blob/main/CHANGELOG.md"
|
15
|
+
}
|
16
|
+
|
11
17
|
# Specify which files should be added to the gem when it is released.
|
12
18
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
13
19
|
ignore_files = %w[
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveRecordQueryCounter
|
4
4
|
# Data structure for storing query information encountered within a block.
|
5
5
|
class Counter
|
6
|
-
attr_accessor :query_count, :row_count, :query_time, :cached_query_count
|
6
|
+
attr_accessor :query_count, :row_count, :query_time, :cached_query_count, :rollback_count
|
7
7
|
attr_reader :thresholds
|
8
8
|
|
9
9
|
def initialize
|
@@ -13,6 +13,7 @@ module ActiveRecordQueryCounter
|
|
13
13
|
@cached_query_count = 0
|
14
14
|
@transactions_hash = {}
|
15
15
|
@thresholds = ActiveRecordQueryCounter.default_thresholds.dup
|
16
|
+
@rollback_count = 0
|
16
17
|
end
|
17
18
|
|
18
19
|
# Return the percentage of queries that used the query cache instead of going to the database.
|
@@ -31,6 +31,7 @@ module ActiveRecordQueryCounter
|
|
31
31
|
if @active_record_query_counter_transaction_start_time && open_transactions == 1
|
32
32
|
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
33
33
|
ActiveRecordQueryCounter.add_transaction(@active_record_query_counter_transaction_start_time, end_time)
|
34
|
+
ActiveRecordQueryCounter.increment_rollbacks
|
34
35
|
@active_record_query_counter_transaction_start_time = nil
|
35
36
|
end
|
36
37
|
super
|
@@ -2,12 +2,9 @@
|
|
2
2
|
|
3
3
|
require_relative "active_record_query_counter/connection_adapter_extension"
|
4
4
|
require_relative "active_record_query_counter/counter"
|
5
|
-
require_relative "active_record_query_counter/rack_middleware"
|
6
|
-
require_relative "active_record_query_counter/sidekiq_middleware"
|
7
5
|
require_relative "active_record_query_counter/thresholds"
|
8
6
|
require_relative "active_record_query_counter/transaction_info"
|
9
7
|
require_relative "active_record_query_counter/transaction_manager_extension"
|
10
|
-
require_relative "active_record_query_counter/version"
|
11
8
|
|
12
9
|
# Everything you need to count ActiveRecord queries and row counts within a block.
|
13
10
|
#
|
@@ -19,6 +16,10 @@ require_relative "active_record_query_counter/version"
|
|
19
16
|
# puts ActiveRecordQueryCounter.row_count
|
20
17
|
# end
|
21
18
|
module ActiveRecordQueryCounter
|
19
|
+
autoload :RackMiddleware, "active_record_query_counter/rack_middleware"
|
20
|
+
autoload :SidekiqMiddleware, "active_record_query_counter/sidekiq_middleware"
|
21
|
+
autoload :VERSION, "active_record_query_counter/version"
|
22
|
+
|
22
23
|
IGNORED_STATEMENTS = %w[SCHEMA EXPLAIN].freeze
|
23
24
|
private_constant :IGNORED_STATEMENTS
|
24
25
|
|
@@ -112,6 +113,17 @@ module ActiveRecordQueryCounter
|
|
112
113
|
end
|
113
114
|
end
|
114
115
|
|
116
|
+
# Return the number of rollbacks that have been counted within the current block.
|
117
|
+
# Returns nil if not inside a block where queries are being counted.
|
118
|
+
#
|
119
|
+
# @return [Integer, nil]
|
120
|
+
def increment_rollbacks
|
121
|
+
counter = current_counter
|
122
|
+
return unless counter.is_a?(Counter)
|
123
|
+
|
124
|
+
counter.rollback_count += 1
|
125
|
+
end
|
126
|
+
|
115
127
|
# Return the number of queries that have been counted within the current block.
|
116
128
|
# Returns nil if not inside a block where queries are being counted.
|
117
129
|
#
|
@@ -194,6 +206,15 @@ module ActiveRecordQueryCounter
|
|
194
206
|
counter.transactions if counter.is_a?(Counter)
|
195
207
|
end
|
196
208
|
|
209
|
+
# Return the number of transactions that have rolled back within the current block.
|
210
|
+
# Returns nil if not inside a block where queries are being counted.
|
211
|
+
#
|
212
|
+
# @return [Integer, nil]
|
213
|
+
def rollback_count
|
214
|
+
counter = current_counter
|
215
|
+
counter.rollback_count if counter.is_a?(Counter)
|
216
|
+
end
|
217
|
+
|
197
218
|
# Return the query info as a hash with keys :query_count, :row_count, :query_time
|
198
219
|
# :transaction_count, and :transaction_type or nil if not inside a block where queries
|
199
220
|
# are being counted.
|
@@ -209,7 +230,8 @@ module ActiveRecordQueryCounter
|
|
209
230
|
cached_query_count: counter.cached_query_count,
|
210
231
|
cache_hit_rate: counter.cache_hit_rate,
|
211
232
|
transaction_count: counter.transaction_count,
|
212
|
-
transaction_time: counter.transaction_time
|
233
|
+
transaction_time: counter.transaction_time,
|
234
|
+
rollback_count: counter.rollback_count
|
213
235
|
}
|
214
236
|
end
|
215
237
|
end
|
@@ -233,8 +255,10 @@ module ActiveRecordQueryCounter
|
|
233
255
|
# @param connection_class [Class] the connection adapter class to extend
|
234
256
|
# @return [void]
|
235
257
|
def enable!(connection_class)
|
236
|
-
|
237
|
-
|
258
|
+
ActiveSupport.on_load(:active_record) do
|
259
|
+
ConnectionAdapterExtension.inject(connection_class)
|
260
|
+
TransactionManagerExtension.inject(ActiveRecord::ConnectionAdapters::TransactionManager)
|
261
|
+
end
|
238
262
|
|
239
263
|
@cache_subscription ||= ActiveSupport::Notifications.subscribe("sql.active_record") do |_name, _start_time, _end_time, _id, payload|
|
240
264
|
if payload[:cached]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_query_counter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Durand
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -46,7 +46,7 @@ extensions: []
|
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
48
|
- CHANGELOG.md
|
49
|
-
- MIT_LICENSE
|
49
|
+
- MIT_LICENSE.txt
|
50
50
|
- README.md
|
51
51
|
- VERSION
|
52
52
|
- active_record_query_counter.gemspec
|
@@ -62,7 +62,10 @@ files:
|
|
62
62
|
homepage: https://github.com/bdurand/active_record_query_counter
|
63
63
|
licenses:
|
64
64
|
- MIT
|
65
|
-
metadata:
|
65
|
+
metadata:
|
66
|
+
homepage_uri: https://github.com/bdurand/active_record_query_counter
|
67
|
+
source_code_uri: https://github.com/bdurand/active_record_query_counter
|
68
|
+
changelog_uri: https://github.com/bdurand/active_record_query_counter/blob/main/CHANGELOG.md
|
66
69
|
post_install_message:
|
67
70
|
rdoc_options: []
|
68
71
|
require_paths:
|
@@ -78,8 +81,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
81
|
- !ruby/object:Gem::Version
|
79
82
|
version: '0'
|
80
83
|
requirements: []
|
81
|
-
rubygems_version: 3.4.
|
84
|
+
rubygems_version: 3.4.10
|
82
85
|
signing_key:
|
83
86
|
specification_version: 4
|
84
|
-
summary:
|
87
|
+
summary: Provides detailed insights into how your code interacts with the database
|
88
|
+
by hooking into ActiveRecord.
|
85
89
|
test_files: []
|
File without changes
|