n_one 1.0.1 → 1.0.2

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: d037a52ad1770b38646937c04fdf274fac7dfdbf5d8351eb5208e0899d4d8927
4
- data.tar.gz: eafb63f23fca359806b398c41480bad4225a46a1806dcbf05d50d88ca8fec197
3
+ metadata.gz: 6ed8520fefb3a0e04185dc4a7f234ec3f9d65d110223dfd52b568d9f1f8d9080
4
+ data.tar.gz: 1164c40f76713cab191569ff896d4309c8938194737ed31cd21897bcc10027d9
5
5
  SHA512:
6
- metadata.gz: 1e18c871f74065f1fac610a11a70717a6bd91caf447062627d91b847602300ac76abae5b7a5200d57dd65ad223f2da6fe6554ff7f0ae97df07710c609d9591e8
7
- data.tar.gz: 4b933072d3e7e62f6abfac60ea794d64049457c756497614ce2f84eb77687d008786e901bdfb1139f5b47a5b49ed9acddafd7cf036e80a6e9620d977bee45a95
6
+ metadata.gz: 98c9dedda0ec0540d55443c678c399dd1bb5bf5b3ee007769b85b6d276aa13bcf14096aa0a8ab310ddedb824df7cd0289a88f0bd952e3f6dd4e707ca0da0ce97
7
+ data.tar.gz: 19da0020d67ef61418399ecfe266c063822d63fcb3912832bc57c135b5df6739aea5d4583927f90e89ecbc557d352cc1bfa99777efdc1cd4ecf6d9983c6a4715
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- n_one (1.0.1)
4
+ n_one (1.0.2)
5
5
  pg_query
6
6
 
7
7
  GEM
@@ -23,8 +23,8 @@ GEM
23
23
  concurrent-ruby (1.1.8)
24
24
  factory_bot (6.1.0)
25
25
  activesupport (>= 5.0.0)
26
- google-protobuf (3.19.1)
27
- google-protobuf (3.19.1-x86_64-linux)
26
+ google-protobuf (3.19.4)
27
+ google-protobuf (3.19.4-x86_64-linux)
28
28
  i18n (1.8.9)
29
29
  concurrent-ruby (~> 1.0)
30
30
  method_source (1.0.0)
@@ -32,8 +32,8 @@ GEM
32
32
  parallel (1.20.1)
33
33
  parser (3.0.1.0)
34
34
  ast (~> 2.4.1)
35
- pg_query (2.1.2)
36
- google-protobuf (>= 3.17.1)
35
+ pg_query (2.1.3)
36
+ google-protobuf (>= 3.19.2)
37
37
  pry (0.14.0)
38
38
  coderay (~> 1.1)
39
39
  method_source (~> 1.0)
data/README.md CHANGED
@@ -74,9 +74,9 @@ end
74
74
  Ignore notifications for call stacks containing one or more substrings:
75
75
 
76
76
  ```ruby
77
- NOne.scan!(whitelist: ['myapp/lib/known_n_plus_ones/']) do
78
- example.run
79
- end
77
+ NOne.scan!(whitelist: ['myapp/lib/known_n_plus_ones/']) do
78
+ example.run
79
+ end
80
80
  ```
81
81
 
82
82
  ## Ignore names
@@ -84,13 +84,42 @@ Ignore notifications for call stacks containing one or more substrings:
84
84
  Ignore queries with names:
85
85
 
86
86
  ```ruby
87
- NOne.scan!(ignore_names: ['SCHEMA']) do
88
- example.run
89
- end
87
+ NOne.scan!(ignore_names: ['SCHEMA']) do
88
+ example.run
89
+ end
90
90
  ```
91
91
 
92
92
  It will skip schema queries(e.g. for column names of a given table)
93
93
 
94
+ ## Stack trace sanitizing
95
+
96
+ Sanitize the call stack trace that is used to calculate the query fingerprint:
97
+
98
+ ```ruby
99
+ sanitizer = lambda do |stacktrace|
100
+ stacktrace.reject { |s| s.include?('/active_record/relation/delegation.rb') }
101
+ end
102
+
103
+ NOne.scan!(stacktrace_sanitizer: sanitizer) do
104
+ example.run
105
+ end
106
+ ```
107
+
108
+ Consider the following example:
109
+
110
+ ```ruby
111
+ class Foo < ActiveRecord::Base
112
+ def self.bar
113
+ first(5)
114
+ end
115
+ end
116
+
117
+ 2.times { Foo.all.bar }
118
+ ```
119
+
120
+ The subsequent `Foo.all.bar` call here will not be recognized as an N+1 query since it will have a different call stack trace (see the reason [here](https://github.com/rails/rails/blob/9a400d808bdbebd5ea50cebc79bde591d2669017/activerecord/lib/active_record/relation/delegation.rb#L82-L85)).
121
+ This can be fixed with the `stacktrace_sanitizer` option as described above.
122
+
94
123
  ## Contributing
95
124
 
96
125
  Bug reports and pull requests are welcome on GitHub at https://github.com/prikha/n_one.
data/lib/n_one/runner.rb CHANGED
@@ -6,10 +6,13 @@ module NOne
6
6
  #
7
7
  # @param whitelist [<String>] frame substrings to be ignored
8
8
  # @param ignore_names [<String>] query names to be ignored
9
+ # @param stacktrace_sanitizer [Proc<Array<String>>] if given, used to filter the stack trace
10
+ # that is used to calculate the location key
9
11
  #
10
- def initialize(whitelist: [], ignore_names: [])
12
+ def initialize(whitelist: [], ignore_names: [], stacktrace_sanitizer: nil)
11
13
  @whitelist = ['active_record/validations/uniqueness'] + whitelist
12
14
  @ignore_names = ignore_names
15
+ @stacktrace_sanitizer = stacktrace_sanitizer
13
16
  end
14
17
 
15
18
  def scan(&block)
@@ -51,7 +54,7 @@ module NOne
51
54
  end.compact
52
55
  end
53
56
 
54
- def record_sql(&block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
57
+ def record_sql(&block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
55
58
  subscriber = ActiveSupport::Notifications.subscribe 'sql.active_record' do |_, _, _, _, data|
56
59
  sql = data[:sql]
57
60
  cached = data[:cached]
@@ -62,7 +65,9 @@ module NOne
62
65
  sql_fingerprint = Query.fingerprint(sql)
63
66
  next unless sql_fingerprint
64
67
 
65
- location_key = Digest::SHA1.hexdigest(caller.join)
68
+ stacktrace = caller
69
+ stacktrace = @stacktrace_sanitizer.call(stacktrace) if @stacktrace_sanitizer
70
+ location_key = Digest::SHA1.hexdigest(stacktrace.join)
66
71
 
67
72
  store["#{sql_fingerprint}_#{location_key}"] ||= {
68
73
  count: 0,
@@ -73,7 +78,7 @@ module NOne
73
78
  store["#{sql_fingerprint}_#{location_key}"][:count] += 1
74
79
  store["#{sql_fingerprint}_#{location_key}"][:sql] << sql
75
80
  store["#{sql_fingerprint}_#{location_key}"][:sql].uniq!
76
- store["#{sql_fingerprint}_#{location_key}"][:caller] ||= caller.dup
81
+ store["#{sql_fingerprint}_#{location_key}"][:caller] ||= stacktrace
77
82
  end
78
83
 
79
84
  block.call
data/lib/n_one/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NOne
4
- VERSION = '1.0.1'
4
+ VERSION = '1.0.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: n_one
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Prikhodko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-24 00:00:00.000000000 Z
11
+ date: 2022-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg_query