n_plus_one_control 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 227fb8257ab67628ffd449aff0358d729dd3ff3eafc291f9fca427aa355e0650
4
- data.tar.gz: 2e65e5f09da3ad989fbb1e2418a657c8142ea8d25540a0daf729adeb9f0b0d5b
3
+ metadata.gz: b20b8f4269aac76f3da642b271b93ddee2adf508539bba6852e02225695de155
4
+ data.tar.gz: e739f342b4d46cc451229f3a065cb0e2b83ee28c301d99045d0f094e74bc137b
5
5
  SHA512:
6
- metadata.gz: e79cf5bd9ec1afd8b10ca184a483edd9e8d8fbbeaca62e147d277102715ead2fa909d720eb0085fc6d2c2441abb5e61aa08f8a081a4b37079b3f552694d3e220
7
- data.tar.gz: 59c76b177dc8b6ad4d58cfbad1f3c3e89204eef6e92d2d39989b5829f714939c77d0d38806e935d3624e20469447607f7b403af2b2bd633a02486e82c6cc4f6c
6
+ metadata.gz: 74805b4ea497ff96bb882557ed8a14e04d32cf5ea5c62e9c65167647c880275aec9d0f8a405a298554e9cfecbb20049d99f630b977475a75122d847c7b27e1a9
7
+ data.tar.gz: 19063ec1fb2a84edcfd0e38075a3858e239a438803ffb40e03916bc8eb5d49dda9e8b03a30a60788e4e00f9c5484811740448fe3241e64658a78ccf884619321
@@ -1,3 +1,9 @@
1
+ ## master (unreleased)
2
+
3
+ ## 0.4.1 (2020-09-04)
4
+
5
+ - Enhance failure message by showing differences in table hits. ([@palkan][])
6
+
1
7
  ## 0.4.0 (2020-07-20)
2
8
 
3
9
  - Make scale factor available in tests via `#current_scale` method. ([@Earendil95][])
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
 
@@ -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: population_proc(populate),
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 population_proc(populate)
43
- (populate || methods.include?(:populate) ? method(:populate) : nil)
42
+ def population_method
43
+ methods.include?(:populate) ? method(:populate) : nil
44
44
  end
45
45
  end
46
46
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NPlusOneControl
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.1"
5
5
  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.0
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-07-20 00:00:00.000000000 Z
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.3
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