Skip to content

Commit 952e9c9

Browse files
authored
Support after-retry reporting to sentry-sidekiq (#1532)
* Improve test setup * Rename process_job to execute_worker * Add config.sidekiq.report_after_job_retries When set to true, `sentry-sidekiq` only reports exceptions when the job has been fully retried. * Update changelog
1 parent ed0b24b commit 952e9c9

File tree

8 files changed

+132
-21
lines changed

8 files changed

+132
-21
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## Unreleased
2+
3+
- Support after-retry reporting to `sentry-sidekiq` [#1532](https://github.com/getsentry/sentry-ruby/pull/1532)
4+
15
## 4.6.5
26

37
- SDK should drop the event when any event processor returns nil [#1523](https://github.com/getsentry/sentry-ruby/pull/1523)

sentry-sidekiq/lib/sentry-sidekiq.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
require "sentry-ruby"
33
require "sentry/integrable"
44
require "sentry/sidekiq/version"
5+
require "sentry/sidekiq/configuration"
56
require "sentry/sidekiq/error_handler"
67
require "sentry/sidekiq/sentry_context_middleware"
78

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module Sentry
2+
class Configuration
3+
attr_reader :sidekiq
4+
5+
add_post_initialization_callback do
6+
@sidekiq = Sentry::Sidekiq::Configuration.new
7+
end
8+
end
9+
10+
module Sidekiq
11+
class Configuration
12+
# Set this option to true if you want Sentry to only capture the last job
13+
# retry if it fails.
14+
attr_accessor :report_after_job_retries
15+
16+
def initialize
17+
@report_after_job_retries = false
18+
end
19+
end
20+
end
21+
end

sentry-sidekiq/lib/sentry/sidekiq/error_handler.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ def call(ex, context)
1111
scope = Sentry.get_current_scope
1212
scope.set_transaction_name(context_filter.transaction_name) unless scope.transaction_name
1313

14+
retry_option = context.dig(:job, "retry")
15+
16+
if Sentry.configuration.sidekiq.report_after_job_retries && retry_option.is_a?(Integer)
17+
retry_count = context.dig(:job, "retry_count")
18+
if retry_count.nil? || retry_count < retry_option - 1
19+
return
20+
end
21+
end
22+
1423
Sentry::Sidekiq.capture_exception(
1524
ex,
1625
contexts: { sidekiq: context_filter.filtered },
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
require "spec_helper"
2+
3+
RSpec.describe Sentry::Sidekiq::Configuration do
4+
it "adds #delayed_job option to Sentry::Configuration" do
5+
config = Sentry::Configuration.new
6+
7+
expect(config.sidekiq).to be_a(described_class)
8+
end
9+
10+
describe "#report_after_job_retries" do
11+
it "has correct default value" do
12+
expect(subject.report_after_job_retries).to eq(false)
13+
end
14+
end
15+
end

sentry-sidekiq/spec/sentry/sidekiq/sentry_context_middleware_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
perform_basic_setup { |config| config.traces_sample_rate = 0 }
2121
Sentry.set_user(user)
2222

23-
process_job(processor, "SadWorker")
23+
execute_worker(processor, SadWorker)
2424

2525
expect(transport.events.count).to eq(1)
2626
event = transport.events.first
@@ -34,7 +34,7 @@
3434
end
3535

3636
it "sets user to the transaction" do
37-
process_job(processor, "HappyWorker")
37+
execute_worker(processor, HappyWorker)
3838

3939
expect(transport.events.count).to eq(1)
4040
transaction = transport.events.first
@@ -43,7 +43,7 @@
4343
end
4444

4545
it "sets user to both the event and transaction" do
46-
process_job(processor, "SadWorker")
46+
execute_worker(processor, SadWorker)
4747

4848
expect(transport.events.count).to eq(2)
4949
transaction = transport.events.first

sentry-sidekiq/spec/sentry/sidekiq_spec.rb

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,24 @@
66
perform_basic_setup
77
end
88

9+
let(:queue) do
10+
Sidekiq::Queue.new("default")
11+
end
12+
13+
let(:retry_set) do
14+
Sidekiq::RetrySet.new
15+
end
16+
17+
before do
18+
retry_set.clear
19+
queue.clear
20+
end
21+
922
after do
1023
# those test jobs will go into the real Redis and be visiable to other sidekiq processes
1124
# this can affect local testing and development, so we should clear them after each test
12-
Sidekiq::RetrySet.new.clear
25+
retry_set.clear
26+
queue.clear
1327
end
1428

1529
let(:processor) do
@@ -28,7 +42,7 @@
2842
end
2943

3044
it "captues exception raised in the worker" do
31-
expect { process_job(processor, "SadWorker") }.to change { transport.events.size }.by(1)
45+
expect { execute_worker(processor, SadWorker) }.to change { transport.events.size }.by(1)
3246

3347
event = transport.events.last.to_hash
3448
expect(event[:sdk]).to eq({ name: "sentry.ruby.sidekiq", version: described_class::VERSION })
@@ -37,8 +51,8 @@
3751

3852
describe "context cleanup" do
3953
it "cleans up context from processed jobs" do
40-
process_job(processor, "HappyWorker")
41-
process_job(processor, "SadWorker")
54+
execute_worker(processor, HappyWorker)
55+
execute_worker(processor, SadWorker)
4256

4357
expect(transport.events.count).to eq(1)
4458
event = transport.events.last.to_json_compatible
@@ -49,8 +63,8 @@
4963
end
5064

5165
it "cleans up context from failed jobs" do
52-
process_job(processor, "SadWorker")
53-
process_job(processor, "VerySadWorker")
66+
execute_worker(processor, SadWorker)
67+
execute_worker(processor, VerySadWorker)
5468

5569
expect(transport.events.count).to eq(2)
5670
event = transport.events.last.to_json_compatible
@@ -61,19 +75,45 @@
6175
end
6276

6377
it "has some context when capturing, even if no exception raised" do
64-
process_job(processor, "ReportingWorker")
78+
execute_worker(processor, ReportingWorker)
6579

6680
event = transport.events.last.to_json_compatible
6781

6882
expect(event["message"]).to eq "I have something to say!"
69-
expect(event["contexts"]["sidekiq"]).to eq("class" => "ReportingWorker", "jid" => "123123", "queue" => "default")
83+
expect(event["contexts"]["sidekiq"]).to eq("args" => [], "class" => "ReportingWorker", "jid" => "123123", "queue" => "default")
7084
end
7185

7286
it "adds the failed job to the retry queue" do
73-
process_job(processor, "SadWorker")
87+
execute_worker(processor, SadWorker)
88+
89+
expect(retry_set.count).to eq(1)
90+
end
7491

75-
retries = Sidekiq::RetrySet.new
76-
expect(retries.count).to eq(1)
92+
context "with config.report_after_job_retries = true" do
93+
before do
94+
Sentry.configuration.sidekiq.report_after_job_retries = true
95+
end
96+
97+
it "doesn't report the error until retries are exhuasted" do
98+
execute_worker(processor, RetryWorker)
99+
100+
expect(transport.events.count).to eq(0)
101+
102+
expect(retry_set.count).to eq(1)
103+
104+
retry_set.first.add_to_queue
105+
job = queue.first
106+
work = Sidekiq::BasicFetch::UnitOfWork.new('queue:default', job.value)
107+
process_work(processor, work)
108+
expect(transport.events.count).to eq(1)
109+
end
110+
111+
it "doesn't affect no-retry jobs" do
112+
execute_worker(processor, SadWorker)
113+
114+
expect(transport.events.count).to eq(1)
115+
expect(retry_set.count).to eq(1)
116+
end
77117
end
78118

79119
context "when tracing is enabled" do
@@ -84,7 +124,7 @@
84124
end
85125

86126
it "records transaction" do
87-
process_job(processor, "HappyWorker")
127+
execute_worker(processor, HappyWorker)
88128

89129
expect(transport.events.count).to eq(1)
90130
transaction = transport.events.first
@@ -96,7 +136,7 @@
96136
end
97137

98138
it "records transaction with exception" do
99-
process_job(processor, "SadWorker")
139+
execute_worker(processor, SadWorker)
100140

101141
expect(transport.events.count).to eq(2)
102142
transaction = transport.events.first

sentry-sidekiq/spec/spec_helper.rb

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require "bundler/setup"
22
require "pry"
3+
require "debug" if RUBY_VERSION.to_f >= 2.6
34

45
# this enables sidekiq's server mode
56
require "sidekiq/cli"
@@ -132,12 +133,32 @@ def perform
132133
end
133134
end
134135

135-
def process_job(processor, klass)
136-
msg = Sidekiq.dump_json(jid: "123123", class: klass)
137-
job = Sidekiq::BasicFetch::UnitOfWork.new('queue:default', msg)
138-
processor.instance_variable_set(:'@job', job)
136+
class RetryWorker
137+
include Sidekiq::Worker
138+
139+
sidekiq_options retry: 1
140+
141+
def perform
142+
1/0
143+
end
144+
end
145+
146+
def execute_worker(processor, klass)
147+
klass_options = klass.sidekiq_options_hash || {}
148+
options = {}
149+
150+
# for Ruby < 2.6
151+
klass_options.each do |k, v|
152+
options[k.to_sym] = v
153+
end
154+
155+
msg = Sidekiq.dump_json(jid: "123123", class: klass, args: [], **options)
156+
work = Sidekiq::BasicFetch::UnitOfWork.new('queue:default', msg)
157+
process_work(processor, work)
158+
end
139159

140-
processor.send(:process, job)
160+
def process_work(processor, work)
161+
processor.send(:process, work)
141162
rescue StandardError
142163
# do nothing
143164
end

0 commit comments

Comments
 (0)