n_plus_one_control 0.4.0 → 0.4.1
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 +6 -0
- data/README.md +11 -3
- data/lib/n_plus_one_control.rb +48 -3
- data/lib/n_plus_one_control/minitest.rb +3 -3
- data/lib/n_plus_one_control/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b20b8f4269aac76f3da642b271b93ddee2adf508539bba6852e02225695de155
|
4
|
+
data.tar.gz: e739f342b4d46cc451229f3a065cb0e2b83ee28c301d99045d0f094e74bc137b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74805b4ea497ff96bb882557ed8a14e04d32cf5ea5c62e9c65167647c880275aec9d0f8a405a298554e9cfecbb20049d99f630b977475a75122d847c7b27e1a9
|
7
|
+
data.tar.gz: 19063ec1fb2a84edcfd0e38075a3858e239a438803ffb40e03916bc8eb5d49dda9e8b03a30a60788e4e00f9c5484811740448fe3241e64658a78ccf884619321
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -186,9 +186,9 @@ If you use caching you can face the problem when first request performs more DB
|
|
186
186
|
|
187
187
|
context "N + 1", :n_plus_one do
|
188
188
|
populate { |n| create_list :post, n }
|
189
|
-
|
189
|
+
|
190
190
|
warmup { get :index } # cache something must be cached
|
191
|
-
|
191
|
+
|
192
192
|
specify do
|
193
193
|
expect { get :index }.to perform_constant_number_of_queries
|
194
194
|
end
|
@@ -215,7 +215,7 @@ end
|
|
215
215
|
def test_no_n_plus_one
|
216
216
|
populate = ->(n) { create_list(:post, n) }
|
217
217
|
warmup = -> { get :index }
|
218
|
-
|
218
|
+
|
219
219
|
assert_perform_constant_number_of_queries population: populate, warmup: warmup do
|
220
220
|
get :index
|
221
221
|
end
|
@@ -240,6 +240,14 @@ NPlusOneControl.default_scale_factors = [2, 3]
|
|
240
240
|
# You can activate verbosity through env variable NPLUSONE_VERBOSE=1
|
241
241
|
NPlusOneControl.verbose = false
|
242
242
|
|
243
|
+
# Print table hits difference, for example:
|
244
|
+
#
|
245
|
+
# Unmatched query numbers by tables:
|
246
|
+
# users (SELECT): 2 != 3
|
247
|
+
# events (INSERT): 1 != 2
|
248
|
+
#
|
249
|
+
self.show_table_stats = true
|
250
|
+
|
243
251
|
# Ignore matching queries
|
244
252
|
NPlusOneControl.ignore = /^(BEGIN|COMMIT|SAVEPOINT|RELEASE)/
|
245
253
|
|
data/lib/n_plus_one_control.rb
CHANGED
@@ -5,17 +5,59 @@ require "n_plus_one_control/executor"
|
|
5
5
|
|
6
6
|
# RSpec and Minitest matchers to prevent N+1 queries problem.
|
7
7
|
module NPlusOneControl
|
8
|
+
# Used to extract a table name from a query
|
9
|
+
EXTRACT_TABLE_RXP = /(insert into|update|delete from|from) ['"](\S+)['"]/i.freeze
|
10
|
+
|
11
|
+
# Used to convert a query part extracted by the regexp above to the corresponding
|
12
|
+
# human-friendly type
|
13
|
+
QUERY_PART_TO_TYPE = {
|
14
|
+
"insert into" => "INSERT",
|
15
|
+
"update" => "UPDATE",
|
16
|
+
"delete from" => "DELETE",
|
17
|
+
"from" => "SELECT"
|
18
|
+
}.freeze
|
19
|
+
|
8
20
|
class << self
|
9
|
-
attr_accessor :default_scale_factors, :verbose, :ignore, :event
|
21
|
+
attr_accessor :default_scale_factors, :verbose, :show_table_stats, :ignore, :event
|
10
22
|
|
11
|
-
def failure_message(queries)
|
23
|
+
def failure_message(queries) # rubocop:disable Metrics/MethodLength
|
12
24
|
msg = ["Expected to make the same number of queries, but got:\n"]
|
13
25
|
queries.each do |(scale, data)|
|
14
26
|
msg << " #{data.size} for N=#{scale}\n"
|
15
|
-
msg << data.map { |sql| " #{sql}\n" }.join.to_s if verbose
|
16
27
|
end
|
28
|
+
|
29
|
+
msg.concat(table_usage_stats(queries.map(&:last))) if show_table_stats
|
30
|
+
|
31
|
+
if verbose
|
32
|
+
queries.each do |(scale, data)|
|
33
|
+
msg << " Queries for N=#{scale}\n"
|
34
|
+
msg << data.map { |sql| " #{sql}\n" }.join.to_s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
17
38
|
msg.join
|
18
39
|
end
|
40
|
+
|
41
|
+
def table_usage_stats(runs) # rubocop:disable Metrics/MethodLength
|
42
|
+
msg = ["\nUnmatched query numbers by tables:\n"]
|
43
|
+
|
44
|
+
before, after = runs.map do |queries|
|
45
|
+
queries.group_by do |query|
|
46
|
+
matches = query.match(EXTRACT_TABLE_RXP)
|
47
|
+
next unless matches
|
48
|
+
|
49
|
+
" #{matches[2]} (#{QUERY_PART_TO_TYPE[matches[1].downcase]})"
|
50
|
+
end.transform_values(&:count)
|
51
|
+
end
|
52
|
+
|
53
|
+
before.keys.each do |k|
|
54
|
+
next if before[k] == after[k]
|
55
|
+
|
56
|
+
msg << "#{k}: #{before[k]} != #{after[k]}\n"
|
57
|
+
end
|
58
|
+
|
59
|
+
msg
|
60
|
+
end
|
19
61
|
end
|
20
62
|
|
21
63
|
# Scale factors to use.
|
@@ -25,6 +67,9 @@ module NPlusOneControl
|
|
25
67
|
# Print performed queries if true
|
26
68
|
self.verbose = ENV['NPLUSONE_VERBOSE'] == '1'
|
27
69
|
|
70
|
+
# Print table hits difference
|
71
|
+
self.show_table_stats = true
|
72
|
+
|
28
73
|
# Ignore matching queries
|
29
74
|
self.ignore = /^(BEGIN|COMMIT|SAVEPOINT|RELEASE)/
|
30
75
|
|
@@ -17,7 +17,7 @@ module NPlusOneControl
|
|
17
17
|
warming_up warmup
|
18
18
|
|
19
19
|
@executor = NPlusOneControl::Executor.new(
|
20
|
-
population:
|
20
|
+
population: populate || population_method,
|
21
21
|
matching: matching || /^SELECT/i,
|
22
22
|
scale_factors: scale_factors || NPlusOneControl.default_scale_factors
|
23
23
|
)
|
@@ -39,8 +39,8 @@ module NPlusOneControl
|
|
39
39
|
(warmup || methods.include?(:warmup) ? method(:warmup) : nil)&.call
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
43
|
-
|
42
|
+
def population_method
|
43
|
+
methods.include?(:populate) ? method(:populate) : nil
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: n_plus_one_control
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- palkan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -181,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
181
181
|
- !ruby/object:Gem::Version
|
182
182
|
version: '0'
|
183
183
|
requirements: []
|
184
|
-
rubygems_version: 3.0.
|
184
|
+
rubygems_version: 3.0.6
|
185
185
|
signing_key:
|
186
186
|
specification_version: 4
|
187
187
|
summary: RSpec and Minitest matchers to prevent N+1 queries problem
|