Skip to content

Commit c9fda4a

Browse files
committed
WIP replace bullet with prosopite for finding performance issues like db n+1 queries
1 parent 41897ea commit c9fda4a

File tree

5 files changed

+84
-9
lines changed

5 files changed

+84
-9
lines changed

Gemfile

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ gem "rswag-api"
4747
gem "rswag-ui"
4848
gem "sablon" # Word document templating tool for Case Court Reports
4949
gem "scout_apm"
50+
gem "scout_apm_logging" # production metrics around speed
5051
gem "sprockets-rails" # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
5152
gem "stimulus-rails"
5253
gem "strong_migrations"
@@ -63,13 +64,13 @@ gem "flipper-ui"
6364
gem "pghero"
6465
gem "pg_query"
6566
group :development, :test do
66-
gem "bullet" # Detect and fix N+1 queries
6767
gem "byebug", platforms: %i[mri mingw x64_mingw] # Call 'byebug' anywhere in the code to stop execution and get a debugger console
6868
gem "dotenv-rails"
6969
gem "factory_bot_rails"
7070
gem "parallel_tests"
7171
gem "pry"
7272
gem "pry-byebug"
73+
gem "prosopite" # check for performance issues like N+1 queries
7374
gem "rspec_junit_formatter"
7475
gem "rspec-rails"
7576
gem "rswag-specs"
@@ -110,5 +111,3 @@ group :test do
110111
gem "simplecov", require: false
111112
gem "webmock" # HTTP request stubber
112113
end
113-
114-
gem "scout_apm_logging", "~> 2.1"

Gemfile.lock

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,6 @@ GEM
113113
bugsnag (6.28.0)
114114
concurrent-ruby (~> 1.0)
115115
builder (3.3.0)
116-
bullet (8.0.8)
117-
activesupport (>= 3.0.0)
118-
uniform_notifier (~> 1.11)
119116
bundler-audit (0.9.2)
120117
bundler (>= 1.2.0, < 3)
121118
thor (~> 1.0)
@@ -418,6 +415,7 @@ GEM
418415
actionpack (>= 7.1)
419416
prettyprint (0.2.0)
420417
prism (1.5.1)
418+
prosopite (2.1.2)
421419
pry (0.15.2)
422420
coderay (~> 1.1)
423421
method_source (~> 1.0)
@@ -657,7 +655,6 @@ GEM
657655
unicode-display_width (3.2.0)
658656
unicode-emoji (~> 4.1)
659657
unicode-emoji (4.1.0)
660-
uniform_notifier (1.17.0)
661658
useragent (0.16.11)
662659
view_component (3.22.0)
663660
activesupport (>= 5.2.0, < 8.1)
@@ -702,7 +699,6 @@ DEPENDENCIES
702699
blueprinter
703700
brakeman
704701
bugsnag
705-
bullet
706702
bundler-audit
707703
byebug
708704
capybara
@@ -746,6 +742,7 @@ DEPENDENCIES
746742
pg_query
747743
pghero
748744
pretender
745+
prosopite
749746
pry
750747
pry-byebug
751748
puma (= 7.0.4)
@@ -770,7 +767,7 @@ DEPENDENCIES
770767
rubocop-rspec_rails
771768
sablon
772769
scout_apm
773-
scout_apm_logging (~> 2.1)
770+
scout_apm_logging
774771
selenium-webdriver
775772
shoulda-matchers
776773
simplecov

app/controllers/application_controller.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ class ApplicationController < ActionController::Base
44
include Organizational
55
include Users::TimeZone
66

7+
unless Rails.env.production?
8+
around_action :n_plus_one_detection
9+
10+
def n_plus_one_detection
11+
Prosopite.scan
12+
yield
13+
ensure
14+
Prosopite.finish
15+
end
16+
end
17+
718
protect_from_forgery
819
before_action :store_user_location!, if: :storable_location?
920
before_action :authenticate_user!

config/initializers/prosopite.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# frozen_string_literal: true
2+
3+
if Rails.configuration.application.prosopite_enabled
4+
require "prosopite/middleware/rack"
5+
Rails.configuration.middleware.use(Prosopite::Middleware::Rack)
6+
end
7+
8+
Rails.application.config.after_initialize do
9+
Prosopite.enabled = Rails.configuration.application.prosopite_enabled
10+
Prosopite.min_n_queries = Rails.configuration.application.prosopite_min_n_queries
11+
Prosopite.rails_logger = true
12+
end

spec/support/prosopite.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# frozen_string_literal: true
2+
3+
Prosopite.enabled = true
4+
Prosopite.raise = true # Fail specs on N+1 detection
5+
Prosopite.rails_logger = true
6+
Prosopite.prosopite_logger = true
7+
Prosopite.allow_stack_paths = [
8+
"shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb",
9+
"shoulda/matchers/active_model/validate_presence_of_matcher.rb",
10+
"shoulda/matchers/active_model/validate_inclusion_of_matcher.rb",
11+
]
12+
13+
PROSOPITE_PATHS = [
14+
"./spec/models/*"
15+
].freeze
16+
17+
# Monkey-patch FactoryBot to pause Prosopite during factory creation
18+
# This prevents N+1 detection in factory callbacks, focusing on actual test code
19+
module FactoryBot
20+
module Strategy
21+
class Create
22+
alias_method :original_result, :result
23+
24+
def result(evaluation)
25+
if defined?(Prosopite) && Prosopite.enabled?
26+
Prosopite.pause do
27+
original_result(evaluation)
28+
end
29+
else
30+
original_result(evaluation)
31+
end
32+
end
33+
end
34+
end
35+
end
36+
37+
RSpec.configure do |config|
38+
config.around do |example|
39+
should_enable = PROSOPITE_PATHS.any? { |pattern|
40+
File.fnmatch?(pattern, example.metadata[:rerun_file_path]
41+
)
42+
}
43+
44+
if should_enable && !example.metadata[:disable_prosopite]
45+
Prosopite.scan do
46+
example.run
47+
end
48+
else
49+
# Disable prosopite globally for this test (works across threads)
50+
original_enabled = Prosopite.enabled?
51+
Prosopite.enabled = false
52+
example.run
53+
Prosopite.enabled = original_enabled
54+
end
55+
end
56+
end

0 commit comments

Comments
 (0)