From 3df7854b389678f8b72447c3e27d3742ee96d182 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Fri, 23 Dec 2022 15:57:09 +0000 Subject: [PATCH 1/8] Update Maze Runner version --- Gemfile-maze-runner | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile-maze-runner b/Gemfile-maze-runner index 05bb7a68..8dddc7ee 100644 --- a/Gemfile-maze-runner +++ b/Gemfile-maze-runner @@ -1,3 +1,3 @@ source "https://rubygems.org" -gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v7.6.0' +gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v7.10.1' From 3760494bdff450dada02e04d9680be1323830056 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Fri, 23 Dec 2022 16:17:25 +0000 Subject: [PATCH 2/8] Use docker compose exec in Que tests --- features/fixtures/que/Dockerfile | 2 ++ features/que.feature | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/features/fixtures/que/Dockerfile b/features/fixtures/que/Dockerfile index a7244b02..cabf13b7 100644 --- a/features/fixtures/que/Dockerfile +++ b/features/fixtures/que/Dockerfile @@ -13,3 +13,5 @@ ENV QUE_VERSION $QUE_VERSION RUN bundle install COPY app/ /usr/src/app + +CMD bundle exec que ./app.rb diff --git a/features/que.feature b/features/que.feature index 9e872f7d..c143b8c6 100644 --- a/features/que.feature +++ b/features/que.feature @@ -1,8 +1,8 @@ Feature: Errors are delivered to Bugsnag from Que Scenario: Que will deliver unhandled errors - Given I run the service "que" with the command "bundle exec ruby app.rb unhandled" - And I run the service "que" with the command "timeout 5 bundle exec que ./app.rb" + Given I start the service "que" + When I execute the command "bundle exec ruby app.rb unhandled" in the service "que" And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is true @@ -14,8 +14,8 @@ Scenario: Que will deliver unhandled errors And the exception "errorClass" equals "RuntimeError" Scenario: Que will deliver handled errors - Given I run the service "que" with the command "bundle exec ruby app.rb handled" - And I run the service "que" with the command "timeout 5 bundle exec que ./app.rb" + Given I start the service "que" + When I execute the command "bundle exec ruby app.rb handled" in the service "que" And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is false From acc49b05bdb83b04b5751a2f38285f443aebb1be Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Fri, 23 Dec 2022 16:26:34 +0000 Subject: [PATCH 3/8] Use docker compose exec in Sidekiq tests --- features/fixtures/sidekiq/Dockerfile | 4 +++- features/sidekiq.feature | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/features/fixtures/sidekiq/Dockerfile b/features/fixtures/sidekiq/Dockerfile index 84c87852..8fb1ce2a 100644 --- a/features/fixtures/sidekiq/Dockerfile +++ b/features/fixtures/sidekiq/Dockerfile @@ -14,4 +14,6 @@ ENV SIDEKIQ_VERSION $SIDEKIQ_VERSION RUN bundle install -COPY app/ /app/ \ No newline at end of file +COPY app/ /app/ + +CMD bundle exec sidekiq -r ./app.rb diff --git a/features/sidekiq.feature b/features/sidekiq.feature index f3f39884..181f0480 100644 --- a/features/sidekiq.feature +++ b/features/sidekiq.feature @@ -1,7 +1,8 @@ Feature: Bugsnag raises errors in Sidekiq workers Scenario: An unhandled RuntimeError sends a report - Given I run the service "sidekiq" with the command "timeout 5 bundle exec rake sidekiq_tests:unhandled_error" + Given I start the service "sidekiq" + And I execute the command "bundle exec ruby initializers/UnhandledError.rb" in the service "sidekiq" And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is true @@ -19,7 +20,8 @@ Scenario: An unhandled RuntimeError sends a report And the event "metaData.config.delivery_method" equals "thread_queue" Scenario: A handled RuntimeError can be notified - Given I run the service "sidekiq" with the command "timeout 5 bundle exec rake sidekiq_tests:handled_error" + Given I start the service "sidekiq" + And I execute the command "bundle exec ruby initializers/HandledError.rb" in the service "sidekiq" And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is false @@ -35,7 +37,8 @@ Scenario: A handled RuntimeError can be notified Scenario: Synchronous delivery can be used Given I set environment variable "BUGSNAG_DELIVERY_METHOD" to "synchronous" - And I run the service "sidekiq" with the command "timeout 5 bundle exec rake sidekiq_tests:handled_error" + And I start the service "sidekiq" + And I execute the command "bundle exec ruby initializers/HandledError.rb" in the service "sidekiq" And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is false From 57a91a93f8280e3c04734a0a94191e0a1d89f91f Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 4 Jan 2023 12:40:40 +0000 Subject: [PATCH 4/8] Use docker compose exec in Rails integrations --- .../app/lib/tasks/fixture.rake | 13 +++ features/rails_features/integrations.feature | 86 +++++++++++-------- features/steps/ruby_notifier_steps.rb | 24 +++++- 3 files changed, 85 insertions(+), 38 deletions(-) create mode 100644 features/fixtures/rails_integrations/app/lib/tasks/fixture.rake diff --git a/features/fixtures/rails_integrations/app/lib/tasks/fixture.rake b/features/fixtures/rails_integrations/app/lib/tasks/fixture.rake new file mode 100644 index 00000000..620e946c --- /dev/null +++ b/features/fixtures/rails_integrations/app/lib/tasks/fixture.rake @@ -0,0 +1,13 @@ +namespace :fixture do + task queue_unhandled_job: [:environment] do + UnhandledJob.perform_later(1, yes: true) + end + + task queue_working_job: [:environment] do + WorkingJob.perform_later + end + + task queue_resque_job: [:environment] do + Resque.enqueue(ResqueWorker, 123, "abc", [7, 8, 9], x: true, y: false) + end +end diff --git a/features/rails_features/integrations.feature b/features/rails_features/integrations.feature index bd8c9d40..1b465541 100644 --- a/features/rails_features/integrations.feature +++ b/features/rails_features/integrations.feature @@ -1,14 +1,10 @@ Feature: App type is set correctly for integrations in a Rails app -Background: - Given I start the rails service - And I run the "db:prepare" rake task in the rails app - And I run the "db:migrate" rake task in the rails app - @rails_integrations Scenario: Delayed job + Given I start the rails service with the database + And I run the "jobs:work" rake task in the rails app in the background When I run "User.new.delay.raise_the_roof" with the rails runner - And I run the "jobs:workoff" rake task in the rails app And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is true @@ -27,6 +23,7 @@ Scenario: Delayed job @rails_integrations Scenario: Mailman + Given I start the rails service When I run "./run_mailman" in the rails app And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier @@ -41,8 +38,9 @@ Scenario: Mailman @rails_integrations Scenario: Que + Given I start the rails service with the database + And I run "bundle exec que ./config/environment.rb" in the rails app in the background When I run "QueJob.enqueue" with the rails runner - And I run "timeout 5 bundle exec que ./config/environment.rb" in the rails app And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is true @@ -56,6 +54,7 @@ Scenario: Que @rails_integrations Scenario: Rake + Given I start the rails service When I run the "rake_task:raise" rake task in the rails app And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier @@ -71,8 +70,9 @@ Scenario: Rake @rails_integrations Scenario: Resque (no on_exit hooks) - When I run "Resque.enqueue(ResqueWorker, 123, %(abc), x: true, y: false)" with the rails runner - And I run "timeout --signal QUIT 5 bundle exec rake resque:work" in the rails app + Given I start the rails service + And I run the "resque:work" rake task in the rails app in the background + When I run the "fixture:queue_resque_job" rake task in the rails app And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is true @@ -87,8 +87,11 @@ Scenario: Resque (no on_exit hooks) And the event "metaData.payload.class" equals "ResqueWorker" And the event "metaData.payload.args.0" equals 123 And the event "metaData.payload.args.1" equals "abc" - And the event "metaData.payload.args.2.x" is true - And the event "metaData.payload.args.2.y" is false + And the event "metaData.payload.args.2.0" equals 7 + And the event "metaData.payload.args.2.1" equals 8 + And the event "metaData.payload.args.2.2" equals 9 + And the event "metaData.payload.args.3.x" is true + And the event "metaData.payload.args.3.y" is false And the event "metaData.rake_task.name" equals "resque:work" And the event "metaData.rake_task.description" equals "Start a Resque worker" And the event "metaData.rake_task.arguments" is null @@ -96,8 +99,9 @@ Scenario: Resque (no on_exit hooks) @rails_integrations Scenario: Resque (with on_exit hooks) Given I set environment variable "RUN_AT_EXIT_HOOKS" to "1" - When I run "Resque.enqueue(ResqueWorker, %(xyz), [7, 8, 9], a: 4, b: 5)" with the rails runner - And I run "timeout --signal QUIT 5 bundle exec rake resque:work" in the rails app + And I start the rails service + And I run the "resque:work" rake task in the rails app in the background + When I run the "fixture:queue_resque_job" rake task in the rails app And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is true @@ -110,20 +114,22 @@ Scenario: Resque (with on_exit hooks) And the event "metaData.config.delivery_method" equals "thread_queue" And the event "metaData.context" equals "ResqueWorker@crash" And the event "metaData.payload.class" equals "ResqueWorker" - And the event "metaData.payload.args.0" equals "xyz" - And the event "metaData.payload.args.1.0" equals 7 - And the event "metaData.payload.args.1.1" equals 8 - And the event "metaData.payload.args.1.2" equals 9 - And the event "metaData.payload.args.2.a" equals 4 - And the event "metaData.payload.args.2.b" equals 5 + And the event "metaData.payload.args.0" equals 123 + And the event "metaData.payload.args.1" equals "abc" + And the event "metaData.payload.args.2.0" equals 7 + And the event "metaData.payload.args.2.1" equals 8 + And the event "metaData.payload.args.2.2" equals 9 + And the event "metaData.payload.args.3.x" is true + And the event "metaData.payload.args.3.y" is false And the event "metaData.rake_task.name" equals "resque:work" And the event "metaData.rake_task.description" equals "Start a Resque worker" And the event "metaData.rake_task.arguments" is null @rails_integrations Scenario: Sidekiq + Given I start the rails service + And I run "bundle exec sidekiq" in the rails app in the background When I run "SidekiqWorker.perform_async" with the rails runner - And I run "timeout 5 bundle exec sidekiq" in the rails app And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is true @@ -140,8 +146,9 @@ Scenario: Sidekiq @rails_integrations Scenario: Using Sidekiq as the Active Job queue adapter for a job that raises Given I set environment variable "ACTIVE_JOB_QUEUE_ADAPTER" to "sidekiq" - When I run "UnhandledJob.perform_later(1, yes: true)" with the rails runner - And I run "timeout 5 bundle exec sidekiq" in the rails app + And I start the rails service + And I run "bundle exec sidekiq" in the rails app in the background + When I run the "fixture:queue_unhandled_job" rake task in the rails app And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is true @@ -162,8 +169,9 @@ Scenario: Using Sidekiq as the Active Job queue adapter for a job that raises @rails_integrations Scenario: Using Resque as the Active Job queue adapter for a job that raises Given I set environment variable "ACTIVE_JOB_QUEUE_ADAPTER" to "resque" - When I run "UnhandledJob.perform_later(1, yes: true)" with the rails runner - And I run "timeout --signal QUIT 5 bundle exec rake resque:work" in the rails app + And I start the rails service + And I run the "resque:work" rake task in the rails app in the background + When I run the "fixture:queue_unhandled_job" rake task in the rails app And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is true @@ -183,8 +191,9 @@ Scenario: Using Resque as the Active Job queue adapter for a job that raises @rails_integrations Scenario: Using Que as the Active Job queue adapter for a job that raises Given I set environment variable "ACTIVE_JOB_QUEUE_ADAPTER" to "que" - When I run "UnhandledJob.perform_later(1, yes: true)" with the rails runner - And I run "timeout 5 bundle exec que -q default ./config/environment.rb" in the rails app + And I start the rails service with the database + And I run "bundle exec que -q default ./config/environment.rb" in the rails app in the background + When I run the "fixture:queue_unhandled_job" rake task in the rails app And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is true @@ -204,8 +213,9 @@ Scenario: Using Que as the Active Job queue adapter for a job that raises @rails_integrations Scenario: Using Delayed Job as the Active Job queue adapter for a job that raises Given I set environment variable "ACTIVE_JOB_QUEUE_ADAPTER" to "delayed_job" - And I run "UnhandledJob.perform_later(1, yes: true)" with the rails runner - And I run the "jobs:workoff" rake task in the rails app + And I start the rails service with the database + And I run the "jobs:work" rake task in the rails app in the background + When I run the "fixture:queue_unhandled_job" rake task in the rails app And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" notifier And the event "unhandled" is true @@ -225,27 +235,31 @@ Scenario: Using Delayed Job as the Active Job queue adapter for a job that raise @rails_integrations Scenario: Using Sidekiq as the Active Job queue adapter for a job that works Given I set environment variable "ACTIVE_JOB_QUEUE_ADAPTER" to "sidekiq" - And I run "WorkingJob.perform_later" with the rails runner - And I run "timeout 5 bundle exec sidekiq" in the rails app + And I start the rails service + And I run "bundle exec sidekiq" in the rails app in the background + When I run the "fixture:queue_working_job" rake task in the rails app Then I should receive no requests @rails_integrations Scenario: Using Resque as the Active Job queue adapter for a job that works Given I set environment variable "ACTIVE_JOB_QUEUE_ADAPTER" to "resque" - And I run "WorkingJob.perform_later" with the rails runner - And I run "timeout --signal QUIT 5 bundle exec rake resque:work" in the rails app + And I start the rails service + And I run "bundle exec rake resque:work" in the rails app in the background + When I run the "fixture:queue_working_job" rake task in the rails app Then I should receive no requests @rails_integrations Scenario: Using Que as the Active Job queue adapter for a job that works Given I set environment variable "ACTIVE_JOB_QUEUE_ADAPTER" to "que" - And I run "WorkingJob.perform_later" with the rails runner - And I run "timeout 5 bundle exec que -q default ./config/environment.rb" in the rails app + And I start the rails service with the database + And I run "bundle exec que -q default ./config/environment.rb" in the rails app in the background + When I run the "fixture:queue_working_job" rake task in the rails app Then I should receive no requests @rails_integrations Scenario: Using Delayed Job as the Active Job queue adapter for a job that works Given I set environment variable "ACTIVE_JOB_QUEUE_ADAPTER" to "delayed_job" - And I run "WorkingJob.perform_later" with the rails runner - And I run the "jobs:workoff" rake task in the rails app + And I start the rails service with the database + And I run the "jobs:work" rake task in the rails app in the background + When I run the "fixture:queue_working_job" rake task in the rails app Then I should receive no requests diff --git a/features/steps/ruby_notifier_steps.rb b/features/steps/ruby_notifier_steps.rb index 6c264079..1fc8f375 100644 --- a/features/steps/ruby_notifier_steps.rb +++ b/features/steps/ruby_notifier_steps.rb @@ -27,13 +27,27 @@ } end +Given("I start the rails service with the database") do + steps %Q{ + Given I start the rails service + And I run the "db:prepare" rake task in the rails app + And I run the "db:migrate" rake task in the rails app + } +end + When("I navigate to the route {string} on the rails app") do |route| RAILS_FIXTURE.navigate_to(route) end When("I run {string} in the rails app") do |command| steps %Q{ - When I run the service "rails#{ENV['RAILS_VERSION']}" with the command "#{command}" + When I execute the command "#{command}" in the service "rails#{ENV['RAILS_VERSION']}" + } +end + +When("I run {string} in the rails app in the background") do |command| + steps %Q{ + When I execute the command "#{command}" in the service "rails#{ENV['RAILS_VERSION']}" in the background } end @@ -43,9 +57,15 @@ } end +When("I run the {string} rake task in the rails app in the background") do |task| + steps %Q{ + When I run "bundle exec rake #{task}" in the rails app in the background + } +end + When("I run {string} with the rails runner") do |code| steps %Q{ - When I run "bundle exec rails runner '#{code}'" in the rails app + When I execute the command "bundle exec rails runner #{code}" in the service "rails#{ENV['RAILS_VERSION']}" } end From 90c365f35fdae4f7fd14138d49db2ee8f8c610db Mon Sep 17 00:00:00 2001 From: Sam Bostock Date: Wed, 25 Jan 2023 16:12:28 -0500 Subject: [PATCH 5/8] Optimize auto_notify boolean check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current implementation checks if `auto_notify` is an instance of `TrueClass` or `FalseClass` (in that order). As this is a "hot path" as far as Bugsnag is concerned (runs on all notifications), we can consider applying the following optimizations: - `true` and `false` are singleton instances of `TrueClass` and `FalseClass`, respectively. Therefore, we can rely on the slightly faster `.equal?` check, which relies on object identity. - `false` is the default for the method, so is likely the most common usage, so we can invert the order of the check and check for `false` before `true`, to benefit from short circuiting in the more common case. This new implementation is faster in the `false` and non-boolean cases, and while it is slower in the `true` case (because of the order inversion), it is still faster than the previous approach was in the `false` case. We can test this using the following benchmark: require "benchmark/ips" [ false, # Default – Probably most common case true, # Override – Probably accounts for almost all other usage {}, # Deprecated – Probably barely any usage ].freeze.each do |auto_notify| Benchmark.ips do |x| x.compare! x.report("#{auto_notify}.is_a? TrueClass or #{auto_notify}.is_a? FalseClass (status quo)") do auto_notify.is_a? TrueClass or auto_notify.is_a? FalseClass end x.report("false.equal? #{auto_notify} or true.equal? #{auto_notify} (proposed) ") do false.equal? auto_notify or true.equal? auto_notify end end end which produces the following results: Implementation |`false` |`true` |`{}` ----------------------------------------------------------------------------|--------------------|--------------------|-------------------- `auto_notify.is_a? TrueClass or auto_notify.is_a? FalseClass` _(status quo)_|11.978M (± 0.6%) i/s|17.122M (± 1.3%) i/s|11.814M (± 0.5%) i/s `false.equal? auto_notify or true.equal? auto_notify` _(proposal)_ |17.955M (± 0.4%) i/s|13.553M (± 1.6%) i/s|13.420M (± 2.5%) i/s --- lib/bugsnag.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bugsnag.rb b/lib/bugsnag.rb index a25ed6b4..5d002a90 100644 --- a/lib/bugsnag.rb +++ b/lib/bugsnag.rb @@ -78,7 +78,7 @@ def configure(validate_api_key=true) # # Optionally accepts a block to append metadata to the yielded report. def notify(exception, auto_notify=false, &block) - unless auto_notify.is_a? TrueClass or auto_notify.is_a? FalseClass + unless false.equal? auto_notify or true.equal? auto_notify configuration.warn("Adding metadata/severity using a hash is no longer supported, please use block syntax instead") auto_notify = false end From f23901c198c99cef919a11ebfca88b260210828b Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 1 Feb 2023 09:46:39 +0000 Subject: [PATCH 6/8] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65fe0fdf..35066185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ Changelog ========= +## TBD + +### Enhancements + +* Improve performance of `Bugsnag.notify` + | [#774](https://github.com/bugsnag/bugsnag-ruby/pull/774) + | [sambostock](https://github.com/sambostock) + ## v6.25.1 (5 January 2023) ### Fixes From d1cc3a49448005649b7a3b1c28cd772adf820320 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 1 Feb 2023 11:16:12 +0000 Subject: [PATCH 7/8] Bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 41ce415f..aec9b105 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.25.1 +6.25.2 From 7454f4b4483b3240e1d2d004a69877d1e63f4b64 Mon Sep 17 00:00:00 2001 From: Joe Haines Date: Wed, 1 Feb 2023 11:16:25 +0000 Subject: [PATCH 8/8] Add version & date to changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35066185..73bf3e48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Changelog ========= -## TBD +## v6.25.2 (7 February 2023) ### Enhancements