Skip to content

Commit ceba0da

Browse files
Reset trace and transaction for sidekiq-cron
Fixes #2391
1 parent 9446a30 commit ceba0da

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Add `include_sentry_event` matcher for RSpec [#2424](https://github.com/getsentry/sentry-ruby/pull/2424)
66
- Add support for Sentry Cache instrumentation, when using Rails.cache ([#2380](https://github.com/getsentry/sentry-ruby/pull/2380))
77
- Add support for Queue Instrumentation for Sidekiq. [#2403](https://github.com/getsentry/sentry-ruby/pull/2403)
8+
- Reset trace_id and add root transaction for sidekiq-cron [#2446](https://github.com/getsentry/sentry-ruby/pull/2446)
89

910
Note: MemoryStore and FileStore require Rails 8.0+
1011

sentry-sidekiq/lib/sentry/sidekiq/cron/job.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,32 @@ module Sentry
1212
module Sidekiq
1313
module Cron
1414
module Job
15+
def self.enqueueing_method
16+
::Sidekiq::Cron::Job.instance_methods.include?(:enque!) ? :enque! : :enqueue!
17+
end
18+
19+
define_method(enqueueing_method) do |*args|
20+
# make sure the current thread has a clean hub
21+
Sentry.clone_hub_to_current_thread
22+
23+
Sentry.with_scope do |scope|
24+
Sentry.with_session_tracking do
25+
begin
26+
scope.set_transaction_name("#{name} (#{klass})")
27+
28+
transaction = start_transaction(scope)
29+
scope.set_span(transaction) if transaction
30+
super(*args)
31+
32+
finish_transaction(transaction, 200)
33+
rescue
34+
finish_transaction(transaction, 500)
35+
raise
36+
end
37+
end
38+
end
39+
end
40+
1541
def save
1642
# validation failed, do nothing
1743
return false unless super
@@ -34,6 +60,22 @@ def save
3460

3561
true
3662
end
63+
64+
def start_transaction(scope)
65+
Sentry.start_transaction(
66+
name: scope.transaction_name,
67+
source: scope.transaction_source,
68+
op: "queue.sidekiq-cron",
69+
origin: "auto.queue.sidekiq.cron"
70+
)
71+
end
72+
73+
def finish_transaction(transaction, status_code)
74+
return unless transaction
75+
76+
transaction.set_http_status(status_code)
77+
transaction.finish
78+
end
3779
end
3880
end
3981
end

sentry-sidekiq/spec/sentry/sidekiq/cron/job_spec.rb

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,24 @@
55
return unless defined?(Sidekiq::Cron::Job)
66

77
RSpec.describe Sentry::Sidekiq::Cron::Job do
8+
let(:processor) do
9+
new_processor
10+
end
11+
12+
let(:transport) do
13+
Sentry.get_current_client.transport
14+
end
15+
816
before do
9-
perform_basic_setup { |c| c.enabled_patches += [:sidekiq_cron] }
17+
perform_basic_setup do |c|
18+
c.enabled_patches += [:sidekiq_cron]
19+
c.traces_sample_rate = 1.0
20+
end
1021
end
1122

1223
before do
24+
Sidekiq::Cron::Job.destroy_all!
25+
Sidekiq::Queue.all.each(&:clear)
1326
schedule_file = 'spec/fixtures/sidekiq-cron-schedule.yml'
1427
schedule = Sidekiq::Cron::Support.load_yaml(ERB.new(IO.read(schedule_file)).result)
1528
schedule = schedule.merge(symbol_name: { cron: '* * * * *', class: HappyWorkerWithSymbolName })
@@ -79,4 +92,37 @@
7992
it 'does not patch ReportingWorker because of invalid schedule' do
8093
expect(ReportingWorker.ancestors).not_to include(Sentry::Cron::MonitorSchedule)
8194
end
95+
96+
describe 'sidekiq-cron' do
97+
it 'adds job to sidekiq within transaction' do
98+
job = Sidekiq::Cron::Job.new(name: 'test', cron: 'not a crontab', class: 'HappyWorkerForCron')
99+
job.send(Sentry::Sidekiq::Cron::Job.enqueueing_method)
100+
101+
expect(::Sidekiq::Queue.new.size).to eq(1)
102+
expect(transport.events.count).to eq(1)
103+
event = transport.events.last
104+
expect(event.spans.count).to eq(1)
105+
expect(event.spans[0][:op]).to eq("queue.publish")
106+
expect(event.spans[0][:data]['messaging.destination.name']).to eq('default')
107+
end
108+
109+
it 'adds job to sidekiq within transaction' do
110+
job = Sidekiq::Cron::Job.new(name: 'test', cron: 'not a crontab', class: 'HappyWorkerForCron')
111+
job.send(Sentry::Sidekiq::Cron::Job.enqueueing_method)
112+
# Time passes.
113+
job.send(Sentry::Sidekiq::Cron::Job.enqueueing_method)
114+
115+
expect(::Sidekiq::Queue.new.size).to eq(2)
116+
expect(transport.events.count).to eq(2)
117+
events = transport.events
118+
expect(events[0].spans.count).to eq(1)
119+
expect(events[0].spans[0][:op]).to eq("queue.publish")
120+
expect(events[0].spans[0][:data]['messaging.destination.name']).to eq('default')
121+
expect(events[1].spans.count).to eq(1)
122+
expect(events[1].spans[0][:op]).to eq("queue.publish")
123+
expect(events[1].spans[0][:data]['messaging.destination.name']).to eq('default')
124+
125+
expect(events[0].dynamic_sampling_context['trace_id']).to_not eq(events[1].dynamic_sampling_context['trace_id'])
126+
end
127+
end
82128
end

0 commit comments

Comments
 (0)