active_record_query_counter 1.1.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -0
- data/README.md +153 -6
- data/VERSION +1 -1
- data/active_record_query_counter.gemspec +8 -6
- data/lib/active_record_query_counter/connection_adapter_extension.rb +16 -0
- data/lib/active_record_query_counter/counter.rb +75 -0
- data/lib/active_record_query_counter/rack_middleware.rb +25 -0
- data/lib/active_record_query_counter/sidekiq_middleware.rb +44 -0
- data/lib/active_record_query_counter/thresholds.rb +48 -0
- data/lib/active_record_query_counter/transaction_info.rb +22 -0
- data/lib/active_record_query_counter/transaction_manager_extension.rb +31 -0
- data/lib/active_record_query_counter/version.rb +5 -0
- data/lib/active_record_query_counter.rb +153 -103
- metadata +19 -42
- data/.github/dependabot.yml +0 -12
- data/.github/workflows/continuous_integration.yml +0 -51
- data/.standard.yml +0 -11
- data/CHANGE_LOG.md +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb19f1f7e8a7678f3cdf3079b05e9aa26353b33b809c240943e0899f584dac6a
|
4
|
+
data.tar.gz: 7bcbce0b2d06c4f871074898651071f1400cd87c7ee8ee514837ee80caac6774
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80c468ec96c069bb4043a0e210d4fb4b1d82714d2aa0b69bd07ac9fd23bb04255ffcf4b175fcfa66b7aa0afd8434c211645e70f4fd8d05ad9c10faa4ee95c679
|
7
|
+
data.tar.gz: fb972d526b98c97f98230a29eb9412726746a58c3d4a05406ab9ccc046d6eb0ec5007fa8d52d26a40c9a966ad2af4c50c045c1d9257cd15ce90dd21c5dedbcfa
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## 2.0.0
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Added capability to send ActiveSupport notifications when query thresholds are exceeded.
|
12
|
+
|
13
|
+
### Changed
|
14
|
+
|
15
|
+
- Calculate elapsed time using monotonic time rather than wall clock time.
|
16
|
+
- Schema queries to get the table structure and explain plan queries are no longer counted.
|
17
|
+
- **Breaking change**: transaction information is now returned in an array of `ActiveRecordQueryCounter::TransactionInfo` objects.
|
18
|
+
- **Breaking change**: internal API for tracking queries and transactions has changed
|
19
|
+
|
20
|
+
## 1.1.2
|
21
|
+
|
22
|
+
### Added
|
23
|
+
|
24
|
+
- Ruby 3.0 compatibility
|
25
|
+
|
26
|
+
### Removed
|
27
|
+
|
28
|
+
- Dropped support for ActiveRecord 4.2
|
29
|
+
|
30
|
+
## 1.1.1
|
31
|
+
### Added
|
32
|
+
|
33
|
+
- Expose stack traces where transactions are being committed.
|
34
|
+
|
35
|
+
## 1.1.0
|
36
|
+
### Added
|
37
|
+
|
38
|
+
- Add number of transactions to statistics being tracked.
|
39
|
+
|
40
|
+
## 1.0.0
|
41
|
+
### Added
|
42
|
+
|
43
|
+
- Track stats about queries run by ActiveRecord within a block.
|
data/README.md
CHANGED
@@ -1,12 +1,19 @@
|
|
1
1
|
# ActiveRecordQueryCounter
|
2
2
|
|
3
3
|
![Continuous Integration](https://github.com/bdurand/active_record_query_counter/workflows/Continuous%20Integration/badge.svg)
|
4
|
-
[![Maintainability](https://api.codeclimate.com/v1/badges/21094ecec0c151983bb1/maintainability)](https://codeclimate.com/github/bdurand/active_record_query_counter/maintainability)
|
5
4
|
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
|
6
5
|
|
7
|
-
This gem injects itself into ActiveRecord to
|
6
|
+
This gem injects itself into ActiveRecord to give you insight into how your code is using the database.
|
8
7
|
|
9
|
-
|
8
|
+
Within a block of code, it will count:
|
9
|
+
|
10
|
+
- the number of queries
|
11
|
+
- the number of rows returned
|
12
|
+
- the amount of time spent on queries
|
13
|
+
- the number of transactions used
|
14
|
+
- the amount of time spent inside transactions
|
15
|
+
|
16
|
+
The intended use is to gather instrumentation stats for finding hot spots in your code that produce a lot of queries or slow queries or queries that return a lot of rows. It can also be used to find code that is not using transactions when making multiple updates to the database.
|
10
17
|
|
11
18
|
## Usage
|
12
19
|
|
@@ -37,9 +44,7 @@ ActiveRecordQueryCounter.count_queries do
|
|
37
44
|
end
|
38
45
|
```
|
39
46
|
|
40
|
-
This gem includes middleware for both Rack and Sidekiq that will enable query counting.
|
41
|
-
|
42
|
-
If you are using Rails with Sidekiq, you can enable both with an initializer.
|
47
|
+
This gem includes middleware for both Rack and Sidekiq that will enable query counting on web requests and in workers. If you are using Rails with Sidekiq, you can enable both with an initializer.
|
43
48
|
|
44
49
|
```ruby
|
45
50
|
ActiveSupport.on_load(:active_record) do
|
@@ -54,3 +59,145 @@ Sidekiq.configure_server do |config|
|
|
54
59
|
end
|
55
60
|
end
|
56
61
|
```
|
62
|
+
|
63
|
+
### Notifications
|
64
|
+
|
65
|
+
You can also subscribe to ActiveSupport notifications to get notified when query thresholds are exceeded.
|
66
|
+
|
67
|
+
#### active_record_query_counter.query_time notification
|
68
|
+
|
69
|
+
This notification is triggered when a query takes longer than the `query_time` threshold. The payload contains the following keys:
|
70
|
+
|
71
|
+
- `:sql` - The SQL statement that was executed.
|
72
|
+
- `:binds` - The bind parameters that were used.
|
73
|
+
- `:row_count` - The number of rows returned.
|
74
|
+
- `:trace` - The stack trace of where the query was executed.
|
75
|
+
|
76
|
+
#### active_record_query_counter.row_count notification
|
77
|
+
|
78
|
+
This notification is triggered when a query returns more rows than the `row_count` threshold. The payload contains the following keys:
|
79
|
+
|
80
|
+
- `:sql` - The SQL statement that was executed.
|
81
|
+
- `:binds` - The bind parameters that were used.
|
82
|
+
- `:row_count` - The number of rows returned.
|
83
|
+
- `:trace` - The stack trace of where the query was executed.
|
84
|
+
|
85
|
+
#### active_record_query_counter.transaction_time notification
|
86
|
+
|
87
|
+
This notification is triggered when a transaction takes longer than the `transaction_time` threshold. The payload contains the following keys:
|
88
|
+
|
89
|
+
- `:trace` - The stack trace of where the transaction was completed.
|
90
|
+
|
91
|
+
#### active_record_query_counter.transaction_count notification
|
92
|
+
|
93
|
+
This notification is triggered when a transaction takes longer than the `transaction_count` threshold. The payload contains the following keys:
|
94
|
+
|
95
|
+
- `:transactions` - An array of `ActiveRecordQueryCounter::TransactionInfo` objects.
|
96
|
+
|
97
|
+
The duration of the notification event is the time between when the first transaction was started and the last transaction was completed.
|
98
|
+
|
99
|
+
#### Thresholds
|
100
|
+
|
101
|
+
The thresholds for triggering notifications can be set globally in an initializer:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
ActiveRecordQueryCounter.default_thresholds.set(
|
105
|
+
query_time: 2.0,
|
106
|
+
row_count: 1000,
|
107
|
+
transaction_time: 5.0,
|
108
|
+
transaction_count: 2
|
109
|
+
)
|
110
|
+
```
|
111
|
+
|
112
|
+
They can be set locally inside a `count_queries` block with the `thresholds` object. Local thresholds will override the global thresholds only inside the block and will not change any global state.
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
ActiveRecordQueryCounter.count_queries do
|
116
|
+
ActiveRecordQueryCounter.thresholds.set(
|
117
|
+
query_time: 1.0,
|
118
|
+
row_count: 100,
|
119
|
+
transaction_time: 2.0,
|
120
|
+
transaction_count: 1
|
121
|
+
)
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
You can pass thresholds to individual Sidekiq workers via the `sidekiq_options` on the worker.
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
class MyWorker
|
129
|
+
include Sidekiq::Worker
|
130
|
+
|
131
|
+
sidekiq_options(
|
132
|
+
active_record_query_counter: {
|
133
|
+
thresholds: {
|
134
|
+
query_time: 1.0,
|
135
|
+
row_count: 100,
|
136
|
+
transaction_time: 2.0,
|
137
|
+
transaction_count: 1
|
138
|
+
}
|
139
|
+
}
|
140
|
+
)
|
141
|
+
# You can disable thresholds for the worker by setting `thresholds: false`.
|
142
|
+
|
143
|
+
def perform
|
144
|
+
do_something
|
145
|
+
end
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
You can set separate thresholds on the Rack middleware when you install it.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
Rails.application.config.middleware.use(ActiveRecordQueryCounter::RackMiddleware, thresholds: {
|
153
|
+
query_time: 1.0,
|
154
|
+
row_count: 100,
|
155
|
+
transaction_time: 2.0,
|
156
|
+
transaction_count: 1
|
157
|
+
})
|
158
|
+
```
|
159
|
+
|
160
|
+
#### Example Notification Subscriptions
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
ActiveRecordQueryCounter.default_thresholds.query_time = 1.0
|
164
|
+
ActiveRecordQueryCounter.default_thresholds.row_count = 1000
|
165
|
+
ActiveRecordQueryCounter.default_thresholds.transaction_time = 2.0
|
166
|
+
ActiveRecordQueryCounter.default_thresholds.transaction_count = 1
|
167
|
+
|
168
|
+
ActiveSupport::Notifications.subscribe('active_record_query_counter.query_time') do |*args|
|
169
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
170
|
+
puts "Query time exceeded (#{event.duration}ms): #{event.payload[:sql]}"
|
171
|
+
puts event.payload[:trace].join("\n")
|
172
|
+
end
|
173
|
+
|
174
|
+
ActiveSupport::Notifications.subscribe('active_record_query_counter.row_count') do |*args|
|
175
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
176
|
+
puts "Row count exceeded (#{event.payload[:row_count]} rows): #{event.payload[:sql]}"
|
177
|
+
puts event.payload[:trace].join("\n")
|
178
|
+
end
|
179
|
+
|
180
|
+
ActiveSupport::Notifications.subscribe('active_record_query_counter.transaction_time') do |*args|
|
181
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
182
|
+
puts "Transaction time exceeded (#{event.duration}ms)"
|
183
|
+
puts event.payload[:trace].join("\n")
|
184
|
+
end
|
185
|
+
|
186
|
+
ActiveSupport::Notifications.subscribe('active_record_query_counter.transaction_count') do |*args|
|
187
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
188
|
+
puts "Transaction count exceeded (#{event.payload[:transactions].size} transactions in #{event.duration}ms)"
|
189
|
+
event.payload[:transactions].each do |info|
|
190
|
+
puts info.trace.join("\n")
|
191
|
+
end
|
192
|
+
end
|
193
|
+
```
|
194
|
+
|
195
|
+
## Contributing
|
196
|
+
|
197
|
+
Open a pull request on GitHub.
|
198
|
+
|
199
|
+
Please use the [standardrb](https://github.com/testdouble/standard) syntax and lint your code with `standardrb --fix` before submitting.
|
200
|
+
|
201
|
+
## License
|
202
|
+
|
203
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.0
|
@@ -11,12 +11,14 @@ Gem::Specification.new do |spec|
|
|
11
11
|
# Specify which files should be added to the gem when it is released.
|
12
12
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
13
13
|
ignore_files = %w[
|
14
|
-
.
|
15
|
-
.travis.yml
|
14
|
+
.
|
16
15
|
Appraisals
|
17
16
|
Gemfile
|
18
17
|
Gemfile.lock
|
19
18
|
Rakefile
|
19
|
+
config.ru
|
20
|
+
assets/
|
21
|
+
bin/
|
20
22
|
gemfiles/
|
21
23
|
spec/
|
22
24
|
]
|
@@ -26,9 +28,9 @@ Gem::Specification.new do |spec|
|
|
26
28
|
|
27
29
|
spec.require_paths = ["lib"]
|
28
30
|
|
29
|
-
spec.add_dependency "activerecord", ">=
|
31
|
+
spec.add_dependency "activerecord", ">= 5.0"
|
30
32
|
|
31
|
-
spec.add_development_dependency "bundler"
|
32
|
-
|
33
|
-
spec.
|
33
|
+
spec.add_development_dependency "bundler"
|
34
|
+
|
35
|
+
spec.required_ruby_version = ">= 2.5"
|
34
36
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordQueryCounter
|
4
|
+
# Module to prepend to the connection adapter to inject the counting behavior.
|
5
|
+
module ConnectionAdapterExtension
|
6
|
+
def exec_query(sql, name = nil, binds = [], *args, **kwargs)
|
7
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
8
|
+
result = super
|
9
|
+
if result.is_a?(ActiveRecord::Result)
|
10
|
+
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
11
|
+
ActiveRecordQueryCounter.add_query(sql, name, binds, result.length, start_time, end_time)
|
12
|
+
end
|
13
|
+
result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordQueryCounter
|
4
|
+
# Data structure for storing query information encountered within a block.
|
5
|
+
class Counter
|
6
|
+
attr_accessor :query_count, :row_count, :query_time
|
7
|
+
attr_reader :thresholds
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@query_count = 0
|
11
|
+
@row_count = 0
|
12
|
+
@query_time = 0.0
|
13
|
+
@transactions_hash = {}
|
14
|
+
@thresholds = ActiveRecordQueryCounter.default_thresholds.dup
|
15
|
+
end
|
16
|
+
|
17
|
+
# Return an array of transaction information for any transactions that have been tracked
|
18
|
+
# by the counter.
|
19
|
+
#
|
20
|
+
# @return [Array<ActiveRecordQueryCounter::TransactionInfo>]
|
21
|
+
def transactions
|
22
|
+
@transactions_hash.values.flatten.sort_by(&:start_time)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Add a tracked transaction.
|
26
|
+
#
|
27
|
+
# @param trace [Array<String>] the trace of the transaction
|
28
|
+
# @param start_time [Float] the monotonic time when the transaction began
|
29
|
+
# @param end_time [Float] the monotonic time when the transaction ended
|
30
|
+
# @return [void]
|
31
|
+
# @api private
|
32
|
+
def add_transaction(trace:, start_time:, end_time:)
|
33
|
+
trace_transactions = @transactions_hash[trace]
|
34
|
+
if trace_transactions
|
35
|
+
# Memory optimization so that we don't store duplicate traces for every transaction in a loop.
|
36
|
+
trace = trace_transactions.first.trace
|
37
|
+
else
|
38
|
+
trace_transactions = []
|
39
|
+
@transactions_hash[trace] = trace_transactions
|
40
|
+
end
|
41
|
+
|
42
|
+
trace_transactions << TransactionInfo.new(start_time: start_time, end_time: end_time, trace: trace)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return the number of transactions that have been tracked by the counter.
|
46
|
+
#
|
47
|
+
# @return [Integer]
|
48
|
+
def transaction_count
|
49
|
+
@transactions_hash.values.flatten.size
|
50
|
+
end
|
51
|
+
|
52
|
+
# Return the total time spent in transactions that have been tracked by the counter.
|
53
|
+
#
|
54
|
+
# @return [Float]
|
55
|
+
def transaction_time
|
56
|
+
@transactions_hash.values.flatten.sum(&:elapsed_time)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get the time when the first transaction began.
|
60
|
+
#
|
61
|
+
# @return [Float, nil] the monotonic time when the first transaction began,
|
62
|
+
# or nil if no transactions have been tracked
|
63
|
+
def first_transaction_start_time
|
64
|
+
transactions.first&.start_time
|
65
|
+
end
|
66
|
+
|
67
|
+
# Get the time when the last transaction completed.
|
68
|
+
#
|
69
|
+
# @return [Float, nil] the monotonic time when the first transaction completed,
|
70
|
+
# or nil if no transactions have been tracked
|
71
|
+
def last_transaction_end_time
|
72
|
+
transactions.max_by(&:end_time)&.end_time
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordQueryCounter
|
4
|
+
# Rack middleware to count queries on a request.
|
5
|
+
class RackMiddleware
|
6
|
+
# @param app [Object] The Rack application.
|
7
|
+
# @param thresholds [Hash] Options for the notification thresholds. Valid keys are:
|
8
|
+
# * `:query_time` - The minimum query time to send a notification for.
|
9
|
+
# * `:row_count` - The minimum row count to send a notification for.
|
10
|
+
# * `:transaction_time` - The minimum transaction time to send a notification for.
|
11
|
+
# * `:transaction_count` - The minimum transaction count to send a notification for.
|
12
|
+
def initialize(app, thresholds: nil)
|
13
|
+
@app = app
|
14
|
+
@thresholds = thresholds.dup.freeze if thresholds
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
ActiveRecordQueryCounter.count_queries do
|
19
|
+
ActiveRecordQueryCounter.thresholds.set(@thresholds) if @thresholds
|
20
|
+
|
21
|
+
@app.call(env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordQueryCounter
|
4
|
+
# Sidekiq middleware to count queries on a job.
|
5
|
+
#
|
6
|
+
# Notification thresholds can be set per worker with the `active_record_query_counter.thresholds` key in the
|
7
|
+
# `sidekiq_options` hash. Valid keys are:
|
8
|
+
# * `:query_time` - The minimum query time to send a notification for.
|
9
|
+
# * `:row_count` - The minimum row count to send a notification for.
|
10
|
+
# * `:transaction_time` - The minimum transaction time to send a notification for.
|
11
|
+
# * `:transaction_count` - The minimum transaction count to send a notification for.
|
12
|
+
#
|
13
|
+
# Thresholds can be disabled for a worker by setting `active_record_query_counter.thresholds` to `false`.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
#
|
17
|
+
# class MyWorker
|
18
|
+
# include Sidekiq::Worker
|
19
|
+
#
|
20
|
+
# sidekiq_options active_record_query_counter: {thresholds: {query_time: 1.5}}
|
21
|
+
#
|
22
|
+
# def perform
|
23
|
+
# # ...
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
class SidekiqMiddleware
|
27
|
+
if defined?(Sidekiq::ServerMiddleware)
|
28
|
+
include Sidekiq::ServerMiddleware
|
29
|
+
end
|
30
|
+
|
31
|
+
def call(job_instance, job_payload, queue)
|
32
|
+
ActiveRecordQueryCounter.count_queries do
|
33
|
+
thresholds = job_payload.dig("active_record_query_counter", "thresholds")
|
34
|
+
if thresholds.is_a?(Hash)
|
35
|
+
ActiveRecordQueryCounter.thresholds.set(thresholds)
|
36
|
+
elsif thresholds == false
|
37
|
+
ActiveRecordQueryCounter.thresholds.clear
|
38
|
+
end
|
39
|
+
|
40
|
+
yield
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordQueryCounter
|
4
|
+
# Thresholds for sending notifications based on query time, row count, transaction time, and
|
5
|
+
# transaction count.
|
6
|
+
class Thresholds
|
7
|
+
attr_reader :query_time, :row_count, :transaction_time, :transaction_count
|
8
|
+
|
9
|
+
def query_time=(value)
|
10
|
+
@query_time = value&.to_f
|
11
|
+
end
|
12
|
+
|
13
|
+
def row_count=(value)
|
14
|
+
@row_count = value&.to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
def transaction_time=(value)
|
18
|
+
@transaction_time = value&.to_f
|
19
|
+
end
|
20
|
+
|
21
|
+
def transaction_count=(value)
|
22
|
+
@transaction_count = value&.to_i
|
23
|
+
end
|
24
|
+
|
25
|
+
# Set threshold values from a hash.
|
26
|
+
#
|
27
|
+
# @param attributes [Hash] the attributes to set
|
28
|
+
# @return [void]
|
29
|
+
def set(values)
|
30
|
+
values.each do |key, value|
|
31
|
+
setter = "#{key}="
|
32
|
+
if respond_to?(setter)
|
33
|
+
public_send("#{key}=", value)
|
34
|
+
else
|
35
|
+
raise ArgumentError, "Unknown threshold: #{key}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Clear all threshold values.
|
41
|
+
def clear
|
42
|
+
@query_time = nil
|
43
|
+
@row_count = nil
|
44
|
+
@transaction_time = nil
|
45
|
+
@transaction_count = nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordQueryCounter
|
4
|
+
# Data structure for storing information about a transaction. Note that the start and end
|
5
|
+
# times are monotonic time and not wall clock time.
|
6
|
+
class TransactionInfo
|
7
|
+
attr_reader :start_time, :end_time, :trace
|
8
|
+
|
9
|
+
def initialize(start_time:, end_time:, trace:)
|
10
|
+
@start_time = start_time
|
11
|
+
@end_time = end_time
|
12
|
+
@trace = trace
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return the time spent in the transaction.
|
16
|
+
#
|
17
|
+
# @return [Float]
|
18
|
+
def elapsed_time
|
19
|
+
end_time - start_time
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordQueryCounter
|
4
|
+
# Extension to ActiveRecord::ConnectionAdapters::TransactionManager to count transactions.
|
5
|
+
module TransactionManagerExtension
|
6
|
+
def begin_transaction(*args, **kwargs)
|
7
|
+
if open_transactions == 0
|
8
|
+
@active_record_query_counter_transaction_start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
9
|
+
end
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def commit_transaction(*args)
|
14
|
+
if @active_record_query_counter_transaction_start_time && open_transactions == 1
|
15
|
+
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
16
|
+
ActiveRecordQueryCounter.add_transaction(@active_record_query_counter_transaction_start_time, end_time)
|
17
|
+
@active_record_query_counter_transaction_start_time = nil
|
18
|
+
end
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def rollback_transaction(*args)
|
23
|
+
if @active_record_query_counter_transaction_start_time && open_transactions == 1
|
24
|
+
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
25
|
+
ActiveRecordQueryCounter.add_transaction(@active_record_query_counter_transaction_start_time, end_time)
|
26
|
+
@active_record_query_counter_transaction_start_time = nil
|
27
|
+
end
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,8 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "active_record_query_counter/connection_adapter_extension"
|
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
|
+
require_relative "active_record_query_counter/thresholds"
|
8
|
+
require_relative "active_record_query_counter/transaction_info"
|
9
|
+
require_relative "active_record_query_counter/transaction_manager_extension"
|
10
|
+
require_relative "active_record_query_counter/version"
|
11
|
+
|
3
12
|
# Everything you need to count ActiveRecord queries and row counts within a block.
|
4
13
|
#
|
5
|
-
#
|
14
|
+
# @example
|
6
15
|
#
|
7
16
|
# ActiveRecordQueryCounter.count_queries do
|
8
17
|
# yield
|
@@ -10,104 +19,164 @@
|
|
10
19
|
# puts ActiveRecordQueryCounter.row_count
|
11
20
|
# end
|
12
21
|
module ActiveRecordQueryCounter
|
13
|
-
|
14
|
-
|
15
|
-
attr_reader :transactions
|
16
|
-
|
17
|
-
def initialize
|
18
|
-
@query_count = 0
|
19
|
-
@row_count = 0
|
20
|
-
@query_time = 0.0
|
21
|
-
@transactions = {}
|
22
|
-
end
|
23
|
-
|
24
|
-
def transaction_count
|
25
|
-
@transactions.size
|
26
|
-
end
|
27
|
-
|
28
|
-
def transaction_time
|
29
|
-
@transactions.values.sum { |count, time| time }
|
30
|
-
end
|
31
|
-
end
|
22
|
+
IGNORED_STATEMENTS = %w[CACHE SCHEMA EXPLAIN].freeze
|
23
|
+
private_constant :IGNORED_STATEMENTS
|
32
24
|
|
33
25
|
class << self
|
34
26
|
# Enable query counting within a block.
|
27
|
+
#
|
28
|
+
# @return [Object] the result of the block
|
35
29
|
def count_queries
|
36
|
-
|
30
|
+
save_counter = current_counter
|
37
31
|
begin
|
38
|
-
|
39
|
-
|
32
|
+
counter = Counter.new
|
33
|
+
self.current_counter = counter
|
34
|
+
|
35
|
+
retval = yield
|
36
|
+
|
37
|
+
transaction_count = counter.transaction_count
|
38
|
+
if transaction_count > 0
|
39
|
+
transaction_threshold = (counter.thresholds.transaction_count || -1)
|
40
|
+
if transaction_threshold >= 0 && transaction_count >= transaction_threshold
|
41
|
+
send_notification("transaction_count", counter.first_transaction_start_time, counter.last_transaction_end_time, transactions: counter.transactions)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
retval
|
40
46
|
ensure
|
41
|
-
|
47
|
+
self.current_counter = save_counter
|
42
48
|
end
|
43
49
|
end
|
44
50
|
|
45
|
-
# Increment the query counters
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
51
|
+
# Increment the query counters.
|
52
|
+
#
|
53
|
+
# @param row_count [Integer] the number of rows returned by the query
|
54
|
+
# @param elapsed_time [Float] the time spent executing the query
|
55
|
+
# @return [void]
|
56
|
+
# @api private
|
57
|
+
def add_query(sql, name, binds, row_count, start_time, end_time)
|
58
|
+
return if IGNORED_STATEMENTS.include?(name)
|
59
|
+
|
60
|
+
counter = current_counter
|
61
|
+
return unless counter.is_a?(Counter)
|
62
|
+
|
63
|
+
elapsed_time = end_time - start_time
|
64
|
+
counter.query_count += 1
|
65
|
+
counter.row_count += row_count
|
66
|
+
counter.query_time += elapsed_time
|
67
|
+
|
68
|
+
trace = nil
|
69
|
+
query_time_threshold = (counter.thresholds.query_time || -1)
|
70
|
+
if query_time_threshold >= 0 && elapsed_time >= query_time_threshold
|
71
|
+
trace = backtrace
|
72
|
+
send_notification("query_time", start_time, end_time, sql: sql, binds: binds, row_count: row_count, trace: trace)
|
73
|
+
end
|
74
|
+
|
75
|
+
row_count_threshold = (counter.thresholds.row_count || -1)
|
76
|
+
if row_count_threshold >= 0 && row_count >= row_count_threshold
|
77
|
+
trace ||= backtrace
|
78
|
+
send_notification("row_count", start_time, end_time, sql: sql, binds: binds, row_count: row_count, trace: trace)
|
52
79
|
end
|
53
80
|
end
|
54
81
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
counter.transactions[trace] = info
|
72
|
-
end
|
82
|
+
# Increment the transaction counters.
|
83
|
+
#
|
84
|
+
# @param start_time [Float] the time the transaction started
|
85
|
+
# @param end_time [Float] the time the transaction ended
|
86
|
+
# @return [void]
|
87
|
+
# @api private
|
88
|
+
def add_transaction(start_time, end_time)
|
89
|
+
counter = current_counter
|
90
|
+
return unless counter.is_a?(Counter)
|
91
|
+
|
92
|
+
trace = backtrace
|
93
|
+
counter.add_transaction(trace: trace, start_time: start_time, end_time: end_time)
|
94
|
+
|
95
|
+
transaction_time_threshold = (counter.thresholds.transaction_time || -1)
|
96
|
+
if transaction_time_threshold >= 0 && end_time - start_time >= transaction_time_threshold
|
97
|
+
send_notification("transaction_time", start_time, end_time, trace: backtrace)
|
73
98
|
end
|
74
99
|
end
|
75
100
|
|
101
|
+
# Return the number of queries that have been counted within the current block.
|
102
|
+
# Returns nil if not inside a block where queries are being counted.
|
103
|
+
#
|
104
|
+
# @return [Integer, nil]
|
76
105
|
def query_count
|
77
|
-
counter =
|
106
|
+
counter = current_counter
|
78
107
|
counter.query_count if counter.is_a?(Counter)
|
79
108
|
end
|
80
109
|
|
110
|
+
# Return the number of rows that have been counted within the current block.
|
111
|
+
# Returns nil if not inside a block where queries are being counted.
|
112
|
+
#
|
113
|
+
# @return [Integer, nil]
|
81
114
|
def row_count
|
82
|
-
counter =
|
115
|
+
counter = current_counter
|
83
116
|
counter.row_count if counter.is_a?(Counter)
|
84
117
|
end
|
85
118
|
|
119
|
+
# Return the total time spent executing queries within the current block.
|
120
|
+
# Returns nil if not inside a block where queries are being counted.
|
121
|
+
#
|
122
|
+
# @return [Float, nil]
|
86
123
|
def query_time
|
87
|
-
counter =
|
124
|
+
counter = current_counter
|
88
125
|
counter.query_time if counter.is_a?(Counter)
|
89
126
|
end
|
90
127
|
|
128
|
+
# Return the number of transactions that have been counted within the current block.
|
129
|
+
# Returns nil if not inside a block where queries are being counted.
|
130
|
+
#
|
131
|
+
# @return [Integer, nil]
|
91
132
|
def transaction_count
|
92
|
-
counter =
|
133
|
+
counter = current_counter
|
93
134
|
counter.transaction_count if counter.is_a?(Counter)
|
94
135
|
end
|
95
136
|
|
137
|
+
# Return the total time spent in transactions that have been counted within the current block.
|
138
|
+
# Returns nil if not inside a block where queries are being counted.
|
139
|
+
#
|
140
|
+
# @return [Float, nil]
|
96
141
|
def transaction_time
|
97
|
-
counter =
|
142
|
+
counter = current_counter
|
98
143
|
counter.transaction_time if counter.is_a?(Counter)
|
99
144
|
end
|
100
145
|
|
146
|
+
# Return the time when the first transaction began within the current block.
|
147
|
+
# Returns nil if not inside a block where queries are being counted or there are no transactions.
|
148
|
+
#
|
149
|
+
# @return [Float, nil] the monotonic time when the first transaction began,
|
150
|
+
def first_transaction_start_time
|
151
|
+
counter = current_counter
|
152
|
+
counter.first_transaction_start_time if counter.is_a?(Counter)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Return the time when the last transaction ended within the current block.
|
156
|
+
# Returns nil if not inside a block where queries are being counted or there are no transactions.
|
157
|
+
#
|
158
|
+
# @return [Float, nil] the monotonic time when the last transaction ended,
|
159
|
+
def last_transaction_end_time
|
160
|
+
counter = current_counter
|
161
|
+
counter.transactions.last&.end_time if counter.is_a?(Counter)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Return an array of transaction information for any transactions that have been counted
|
165
|
+
# within the current block. Returns nil if not inside a block where queries are being counted.
|
166
|
+
#
|
167
|
+
# @return [Array<ActiveRecordQueryCounter::TransactionInfo>, nil]
|
101
168
|
def transactions
|
102
|
-
counter =
|
103
|
-
counter.transactions
|
169
|
+
counter = current_counter
|
170
|
+
counter.transactions if counter.is_a?(Counter)
|
104
171
|
end
|
105
172
|
|
106
173
|
# Return the query info as a hash with keys :query_count, :row_count, :query_time
|
107
174
|
# :transaction_count, and :transaction_type or nil if not inside a block where queries
|
108
175
|
# are being counted.
|
176
|
+
#
|
177
|
+
# @return [Hash, nil]
|
109
178
|
def info
|
110
|
-
counter =
|
179
|
+
counter = current_counter
|
111
180
|
if counter.is_a?(Counter)
|
112
181
|
{
|
113
182
|
query_count: counter.query_count,
|
@@ -119,7 +188,24 @@ module ActiveRecordQueryCounter
|
|
119
188
|
end
|
120
189
|
end
|
121
190
|
|
191
|
+
# The global notification thresholds for sending notifications. The values set in these
|
192
|
+
# thresholds are used as the default values.
|
193
|
+
#
|
194
|
+
# @return [ActiveRecordQueryCounter::Thresholds]
|
195
|
+
def default_thresholds
|
196
|
+
@default_thresholds ||= Thresholds.new
|
197
|
+
end
|
198
|
+
|
199
|
+
# Get the current local notification thresholds. These thresholds are only used within
|
200
|
+
# the current `count_queries` block.
|
201
|
+
def thresholds
|
202
|
+
current_counter&.thresholds || default_thresholds.dup
|
203
|
+
end
|
204
|
+
|
122
205
|
# Enable the query counting behavior on a connection adapter class.
|
206
|
+
#
|
207
|
+
# @param connection_class [Class] the connection adapter class to extend
|
208
|
+
# @return [void]
|
123
209
|
def enable!(connection_class)
|
124
210
|
unless connection_class.include?(ConnectionAdapterExtension)
|
125
211
|
connection_class.prepend(ConnectionAdapterExtension)
|
@@ -128,60 +214,24 @@ module ActiveRecordQueryCounter
|
|
128
214
|
ActiveRecord::ConnectionAdapters::TransactionManager.prepend(TransactionManagerExtension)
|
129
215
|
end
|
130
216
|
end
|
131
|
-
end
|
132
217
|
|
133
|
-
|
134
|
-
module ConnectionAdapterExtension
|
135
|
-
def exec_query(*args)
|
136
|
-
start_time = Time.now
|
137
|
-
result = super
|
138
|
-
if result.is_a?(ActiveRecord::Result)
|
139
|
-
ActiveRecordQueryCounter.increment(result.length, Time.now - start_time)
|
140
|
-
end
|
141
|
-
result
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
module TransactionManagerExtension
|
146
|
-
def begin_transaction(*args)
|
147
|
-
if open_transactions == 0
|
148
|
-
@active_record_query_counter_transaction_start_time = Time.current
|
149
|
-
end
|
150
|
-
super
|
151
|
-
end
|
152
|
-
|
153
|
-
def commit_transaction(*args)
|
154
|
-
if @active_record_query_counter_transaction_start_time && open_transactions == 1
|
155
|
-
ActiveRecordQueryCounter.increment_transaction(Time.current - @active_record_query_counter_transaction_start_time)
|
156
|
-
@active_record_query_counter_transaction_start_time = nil
|
157
|
-
end
|
158
|
-
super
|
159
|
-
end
|
218
|
+
private
|
160
219
|
|
161
|
-
def
|
162
|
-
|
163
|
-
ActiveRecordQueryCounter.increment_transaction(Time.current - @active_record_query_counter_transaction_start_time)
|
164
|
-
@active_record_query_counter_transaction_start_time = nil
|
165
|
-
end
|
166
|
-
super
|
220
|
+
def current_counter
|
221
|
+
Thread.current[:active_record_query_counter]
|
167
222
|
end
|
168
|
-
end
|
169
223
|
|
170
|
-
|
171
|
-
|
172
|
-
def initialize(app)
|
173
|
-
@app = app
|
224
|
+
def current_counter=(counter)
|
225
|
+
Thread.current[:active_record_query_counter] = counter
|
174
226
|
end
|
175
227
|
|
176
|
-
def
|
177
|
-
|
228
|
+
def send_notification(name, start_time, end_time, payload = {})
|
229
|
+
id = "#{name}-#{SecureRandom.hex}"
|
230
|
+
ActiveSupport::Notifications.publish("active_record_query_counter.#{name}", start_time, end_time, id, payload)
|
178
231
|
end
|
179
|
-
end
|
180
232
|
|
181
|
-
|
182
|
-
|
183
|
-
def call(worker, job, queue, &block)
|
184
|
-
ActiveRecordQueryCounter.count_queries(&block)
|
233
|
+
def backtrace
|
234
|
+
caller.reject { |line| line.start_with?(__dir__) }
|
185
235
|
end
|
186
236
|
end
|
187
237
|
end
|
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:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Durand
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,44 +16,16 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '2.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '2.0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '3.0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '3.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: sqlite3
|
57
29
|
requirement: !ruby/object:Gem::Requirement
|
58
30
|
requirements:
|
59
31
|
- - ">="
|
@@ -66,27 +38,32 @@ dependencies:
|
|
66
38
|
- - ">="
|
67
39
|
- !ruby/object:Gem::Version
|
68
40
|
version: '0'
|
69
|
-
description:
|
41
|
+
description:
|
70
42
|
email:
|
71
43
|
- bbdurand@gmail.com
|
72
44
|
executables: []
|
73
45
|
extensions: []
|
74
46
|
extra_rdoc_files: []
|
75
47
|
files:
|
76
|
-
-
|
77
|
-
- ".github/workflows/continuous_integration.yml"
|
78
|
-
- ".standard.yml"
|
79
|
-
- CHANGE_LOG.md
|
48
|
+
- CHANGELOG.md
|
80
49
|
- MIT_LICENSE
|
81
50
|
- README.md
|
82
51
|
- VERSION
|
83
52
|
- active_record_query_counter.gemspec
|
84
53
|
- lib/active_record_query_counter.rb
|
54
|
+
- lib/active_record_query_counter/connection_adapter_extension.rb
|
55
|
+
- lib/active_record_query_counter/counter.rb
|
56
|
+
- lib/active_record_query_counter/rack_middleware.rb
|
57
|
+
- lib/active_record_query_counter/sidekiq_middleware.rb
|
58
|
+
- lib/active_record_query_counter/thresholds.rb
|
59
|
+
- lib/active_record_query_counter/transaction_info.rb
|
60
|
+
- lib/active_record_query_counter/transaction_manager_extension.rb
|
61
|
+
- lib/active_record_query_counter/version.rb
|
85
62
|
homepage: https://github.com/bdurand/active_record_query_counter
|
86
63
|
licenses:
|
87
64
|
- MIT
|
88
65
|
metadata: {}
|
89
|
-
post_install_message:
|
66
|
+
post_install_message:
|
90
67
|
rdoc_options: []
|
91
68
|
require_paths:
|
92
69
|
- lib
|
@@ -94,15 +71,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
94
71
|
requirements:
|
95
72
|
- - ">="
|
96
73
|
- !ruby/object:Gem::Version
|
97
|
-
version: '
|
74
|
+
version: '2.5'
|
98
75
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
76
|
requirements:
|
100
77
|
- - ">="
|
101
78
|
- !ruby/object:Gem::Version
|
102
79
|
version: '0'
|
103
80
|
requirements: []
|
104
|
-
rubygems_version: 3.
|
105
|
-
signing_key:
|
81
|
+
rubygems_version: 3.4.10
|
82
|
+
signing_key:
|
106
83
|
specification_version: 4
|
107
84
|
summary: Count total number of ActiveRecord queries and row counts inside a block
|
108
85
|
test_files: []
|
data/.github/dependabot.yml
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
# Dependabot update strategy
|
2
|
-
version: 2
|
3
|
-
updates:
|
4
|
-
- package-ecosystem: bundler
|
5
|
-
directory: "/"
|
6
|
-
schedule:
|
7
|
-
interval: daily
|
8
|
-
allow:
|
9
|
-
# Automatically keep all runtime dependencies updated
|
10
|
-
- dependency-name: "*"
|
11
|
-
dependency-type: "production"
|
12
|
-
versioning-strategy: lockfile-only
|
@@ -1,51 +0,0 @@
|
|
1
|
-
name: Continuous Integration
|
2
|
-
on:
|
3
|
-
push:
|
4
|
-
branches:
|
5
|
-
- master
|
6
|
-
- actions-*
|
7
|
-
tags:
|
8
|
-
- v*
|
9
|
-
pull_request:
|
10
|
-
env:
|
11
|
-
BUNDLE_CLEAN: "true"
|
12
|
-
BUNDLE_PATH: vendor/bundle
|
13
|
-
BUNDLE_JOBS: 3
|
14
|
-
BUNDLE_RETRY: 3
|
15
|
-
jobs:
|
16
|
-
specs:
|
17
|
-
name: ${{ matrix.job }} ruby-${{ matrix.ruby }} ${{ matrix.activerecord && format('activerecord-{0}', matrix.activerecord) }}
|
18
|
-
runs-on: ubuntu-latest
|
19
|
-
strategy:
|
20
|
-
fail-fast: false
|
21
|
-
matrix:
|
22
|
-
ruby: ["2.6"]
|
23
|
-
activerecord: ["4.2", "5.0", "5.1", "5.2", "6.0"]
|
24
|
-
job: [ rspec ]
|
25
|
-
include:
|
26
|
-
- ruby: "2.7"
|
27
|
-
activerecord: original
|
28
|
-
job: rspec
|
29
|
-
- ruby: "2.7"
|
30
|
-
job: standardrb
|
31
|
-
steps:
|
32
|
-
- name: checkout
|
33
|
-
uses: actions/checkout@v2
|
34
|
-
- name: set up Ruby
|
35
|
-
uses: ruby/setup-ruby@v1
|
36
|
-
with:
|
37
|
-
ruby-version: ${{ matrix.ruby }}
|
38
|
-
- name: inject activerecord ${{ matrix.activerecord }}
|
39
|
-
if: matrix.activerecord != 'original' && matrix.activerecord != null
|
40
|
-
run: | # inject a specific version of activerecord into the Gemfile
|
41
|
-
bundle update
|
42
|
-
bundle exec appraisal generate
|
43
|
-
bundle config set gemfile "gemfiles/activerecord_${{ matrix.activerecord }}.gemfile"
|
44
|
-
- name: install dependencies
|
45
|
-
run: bundle install
|
46
|
-
- name: specs
|
47
|
-
if: matrix.job == 'rspec'
|
48
|
-
run: bundle exec rake spec
|
49
|
-
- name: standardrb
|
50
|
-
if: matrix.job == 'standardrb'
|
51
|
-
run: bundle exec rake standard
|
data/.standard.yml
DELETED