prosopite 2.1.1 → 2.2.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/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -0
- data/lib/prosopite/version.rb +1 -1
- data/lib/prosopite.rb +23 -14
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ba705386994a5f73eb64b024eadd45fd4c29db669ccc408283746d03d58bdf8
|
|
4
|
+
data.tar.gz: b3d52ea151dd88b9da262286df321fcdafd2245ce3b09c9539fd54e9ae54ee8d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e6f74f6cd59e7537429f32300072394c428837e3ec0dd8aed971d601f447c0f3b28e2bdd68f240f98906be205d23958c3cdd8cbf181821cc26f4f435de5f17c8
|
|
7
|
+
data.tar.gz: 570b9ed4965908b45b5f1f5d6dc1b41d84e0b00759c93e1ff875aa181f143c445ec910ad96dfb4c52ba2020e4948660fc203d3ff8cd8c7496bc870acf0df8180
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -329,6 +329,7 @@ class ApplicationController < ActionController::Base
|
|
|
329
329
|
end
|
|
330
330
|
end
|
|
331
331
|
```
|
|
332
|
+
|
|
332
333
|
```ruby
|
|
333
334
|
# app/controllers/books_controller.rb
|
|
334
335
|
class BooksController < ApplicationController
|
|
@@ -343,6 +344,7 @@ class BooksController < ApplicationController
|
|
|
343
344
|
@book.reviews.map(&:author) # This will not raise N+1 errors
|
|
344
345
|
end
|
|
345
346
|
end
|
|
347
|
+
```
|
|
346
348
|
|
|
347
349
|
## Custom Logging Configuration
|
|
348
350
|
|
data/lib/prosopite/version.rb
CHANGED
data/lib/prosopite.rb
CHANGED
|
@@ -51,6 +51,7 @@ module Prosopite
|
|
|
51
51
|
tc[:prosopite_query_counter] = Hash.new(0)
|
|
52
52
|
tc[:prosopite_query_holder] = Hash.new { |h, k| h[k] = [] }
|
|
53
53
|
tc[:prosopite_query_caller] = {}
|
|
54
|
+
tc[:prosopite_query_duration] = Hash.new(0.0)
|
|
54
55
|
|
|
55
56
|
@allow_stack_paths ||= []
|
|
56
57
|
@ignore_pauses ||= false
|
|
@@ -111,6 +112,7 @@ module Prosopite
|
|
|
111
112
|
tc[:prosopite_query_counter] = nil
|
|
112
113
|
tc[:prosopite_query_holder] = nil
|
|
113
114
|
tc[:prosopite_query_caller] = nil
|
|
115
|
+
tc[:prosopite_query_duration] = nil
|
|
114
116
|
end
|
|
115
117
|
|
|
116
118
|
def start_raise
|
|
@@ -146,13 +148,14 @@ module Prosopite
|
|
|
146
148
|
|
|
147
149
|
next unless queries.any?
|
|
148
150
|
|
|
149
|
-
kaller = tc[:prosopite_query_caller][location_key]
|
|
151
|
+
kaller = tc[:prosopite_query_caller][location_key].map(&:to_s)
|
|
150
152
|
allow_list = (@allow_stack_paths + DEFAULT_ALLOW_LIST)
|
|
151
153
|
is_allowed = kaller.any? { |f| allow_list.any? { |s| f.match?(s) } }
|
|
152
154
|
|
|
153
155
|
unless is_allowed
|
|
156
|
+
duration_ms = tc[:prosopite_query_duration][location_key]
|
|
154
157
|
queries.each do |q|
|
|
155
|
-
tc[:prosopite_notifications][q] = kaller
|
|
158
|
+
tc[:prosopite_notifications][q] = { kaller: kaller, duration_ms: duration_ms }
|
|
156
159
|
end
|
|
157
160
|
end
|
|
158
161
|
end
|
|
@@ -160,12 +163,7 @@ module Prosopite
|
|
|
160
163
|
end
|
|
161
164
|
|
|
162
165
|
def fingerprint(query)
|
|
163
|
-
|
|
164
|
-
ActiveRecord::Base.lease_connection
|
|
165
|
-
else
|
|
166
|
-
ActiveRecord::Base.connection
|
|
167
|
-
end
|
|
168
|
-
db_adapter = conn.adapter_name.downcase
|
|
166
|
+
db_adapter = ActiveRecord::Base.connection_db_config.adapter
|
|
169
167
|
if db_adapter.include?('mysql') || db_adapter.include?('trilogy')
|
|
170
168
|
mysql_fingerprint(query)
|
|
171
169
|
else
|
|
@@ -236,8 +234,12 @@ module Prosopite
|
|
|
236
234
|
|
|
237
235
|
notifications_str = String.new('')
|
|
238
236
|
|
|
239
|
-
tc[:prosopite_notifications].each do |queries,
|
|
240
|
-
|
|
237
|
+
tc[:prosopite_notifications].each do |queries, info|
|
|
238
|
+
kaller = info[:kaller]
|
|
239
|
+
duration_ms = info[:duration_ms]
|
|
240
|
+
time_str = duration_ms ? " (#{duration_ms.round(1)}ms)" : ''
|
|
241
|
+
|
|
242
|
+
notifications_str << "N+1 queries detected#{time_str}:\n"
|
|
241
243
|
|
|
242
244
|
queries.each { |q| notifications_str << " #{q}\n" }
|
|
243
245
|
|
|
@@ -277,18 +279,25 @@ module Prosopite
|
|
|
277
279
|
@subscribed ||= false
|
|
278
280
|
return if @subscribed
|
|
279
281
|
|
|
280
|
-
ActiveSupport::Notifications.subscribe 'sql.active_record' do |_,
|
|
282
|
+
ActiveSupport::Notifications.subscribe 'sql.active_record' do |_, start, finish, _, data|
|
|
281
283
|
sql, name = data[:sql], data[:name]
|
|
282
284
|
|
|
283
285
|
if scan? && name != "SCHEMA" && sql.include?('SELECT') && data[:cached].nil? && !ignore_query?(sql)
|
|
284
|
-
query_caller =
|
|
285
|
-
|
|
286
|
+
query_caller = caller_locations
|
|
287
|
+
# Calculate the location key with as few allocations as possible
|
|
288
|
+
location_key = [].tap do |array|
|
|
289
|
+
query_caller.each do |loc|
|
|
290
|
+
array << loc.path
|
|
291
|
+
array << loc.lineno
|
|
292
|
+
end
|
|
293
|
+
end.hash
|
|
286
294
|
|
|
287
295
|
tc[:prosopite_query_counter][location_key] += 1
|
|
288
296
|
tc[:prosopite_query_holder][location_key] << sql
|
|
297
|
+
tc[:prosopite_query_duration][location_key] += (finish - start) * 1000 if tc[:prosopite_query_duration]
|
|
289
298
|
|
|
290
299
|
if tc[:prosopite_query_counter][location_key] > 1
|
|
291
|
-
tc[:prosopite_query_caller][location_key] = query_caller
|
|
300
|
+
tc[:prosopite_query_caller][location_key] = query_caller
|
|
292
301
|
end
|
|
293
302
|
end
|
|
294
303
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: prosopite
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mpampis Kostas
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-04-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: pry
|
|
@@ -133,7 +133,7 @@ licenses:
|
|
|
133
133
|
metadata:
|
|
134
134
|
homepage_uri: https://github.com/charkost/prosopite
|
|
135
135
|
source_code_uri: https://github.com/charkost/prosopite
|
|
136
|
-
post_install_message:
|
|
136
|
+
post_install_message:
|
|
137
137
|
rdoc_options: []
|
|
138
138
|
require_paths:
|
|
139
139
|
- lib
|
|
@@ -149,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
149
149
|
version: '0'
|
|
150
150
|
requirements: []
|
|
151
151
|
rubygems_version: 3.5.3
|
|
152
|
-
signing_key:
|
|
152
|
+
signing_key:
|
|
153
153
|
specification_version: 4
|
|
154
154
|
summary: N+1 auto-detection for Rails with zero false positives / false negatives
|
|
155
155
|
test_files: []
|