Description of the bug
Resque::Job instrumentation never pushes spans to the Exporter when using BatchSpanProcessor.
The same setup with SimpleSpanProcessor does work.
Share details about your runtime
Operating system details: Macos Sonoma 14.3.1 (23D60)
RUBY_ENGINE: "ruby"
RUBY_VERSION: "2.7.6"
RUBY_DESCRIPTION: "ruby 2.7.6p219 (2022-04-12 revision c9c2245c0a) [x86_64-darwin22]"
Share a simplified reproduction if possible
# example.rb
require 'bundler/inline'
require 'bundler'
gemfile(true) do
source 'https://rubygems.org'
gem 'opentelemetry-api'
gem 'opentelemetry-sdk'
gem 'resque', '2.0.0'
gem 'redis', '4.6.0'
gem 'opentelemetry-instrumentation-resque', '0.3.1'
end
require 'opentelemetry-api'
require 'opentelemetry-sdk'
class SummarizingExporter < OpenTelemetry::SDK::Trace::Export::ConsoleSpanExporter
def export(spans, timeout: nil)
return OpenTelemetry::SDK::Trace::Export::FAILURE if @stopped
Array(spans).each { |s| summarize_span(s) }
OpenTelemetry::SDK::Trace::Export::SUCCESS
end
def summarize_span(span)
puts "Span(#{span.name}, #{span.attributes.inspect})"
end
end
exporter = SummarizingExporter.new
use_batch = ENV['PROCESSOR_TYPE'] != 'simple' # default to batched
OpenTelemetry::SDK.configure do |c|
c.use('OpenTelemetry::Instrumentation::Resque', span_naming: :job_class)
if use_batch
c.add_span_processor(
OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
exporter,
schedule_delay: 500 #ms
)
)
else
c.add_span_processor(
OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
exporter
)
)
end
end
MainTracer = OpenTelemetry.tracer_provider.tracer('example')
class ExampleJob
@queue = 'example'
def self.perform(arg)
MainTracer.in_span('foo') { puts "performing ExampleJob with #{arg}" }
end
end
require 'redis'
require 'resque'
Redis.silence_deprecations = true
Resque.enqueue(ExampleJob, 12345)
Resque::Worker.new('example').work_one_job
sleep(1) # wait for more than batch processor interval
OpenTelemetry.tracer_provider.shutdown
puts "Finished."
expected behavior:
→ PROCESSOR_TYPE=simple ruby example.rb
...
I, [2024-04-23T11:45:27.080842 #88592] INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Resque was successfully installed with the following options {:span_naming=>:job_class, :propagation_style=>:link}
Span(ExampleJob send, {"messaging.system"=>"resque", "messaging.destination"=>"example", "messaging.destination_kind"=>"queue", "messaging.resque.job_class"=>"ExampleJob"})
performing ExampleJob with 12345
Span(foo, {})
Span(ExampleJob process, {"messaging.system"=>"resque", "messaging.destination"=>"example", "messaging.destination_kind"=>"queue", "messaging.resque.job_class"=>"ExampleJob"})
Finished.
note 3 spans are exported, including foo and ExampleJob process
actual behavior:
→ PROCESSOR_TYPE=batch ruby example.rb
...
I, [2024-04-23T11:45:27.080842 #88592] INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Resque was successfully installed with the following options {:span_naming=>:job_class, :propagation_style=>:link}
performing ExampleJob with 12345
Span(ExampleJob send, {"messaging.system"=>"resque", "messaging.destination"=>"example", "messaging.destination_kind"=>"queue", "messaging.resque.job_class"=>"ExampleJob"})
Finished.
note, only the ExampleJob send span is exported
Notes
I also saw some similar behavior when using SimpleSpanProcessor with InMemorySpanExporter which also uses mutexes to lock resources 🤔 ... perhaps this bug related to thread/multiprocess safety. resque does some strange things with concurrency in its workers. at least in the version above, the default is to fork for every job.
setting FORK_PER_JOB=false in the env actually gives the correct result, but that's not generally desirable, as forking gives you better job isolation, protecting against memory space pollution.
The same behavior happens in the latest version of resque as of this writing (v2.6.0).
I wasn't able to test on ruby 3 due to my local environment being a little borked, but we are stuck on ruby 2.7.6 for now anyway.
Would be nice for this to Just Work out of the box, but in the mean time, if there is a workaround to allow this to export correctly even with FORK_PER_JOB enabled, that would be appreciated.
Thanks!
Description of the bug
Resque::Job instrumentation never pushes spans to the Exporter when using
BatchSpanProcessor.The same setup with
SimpleSpanProcessordoes work.Share details about your runtime
Operating system details: Macos Sonoma 14.3.1 (23D60)
RUBY_ENGINE: "ruby"
RUBY_VERSION: "2.7.6"
RUBY_DESCRIPTION: "ruby 2.7.6p219 (2022-04-12 revision c9c2245c0a) [x86_64-darwin22]"
Share a simplified reproduction if possible
expected behavior:
note 3 spans are exported, including
fooandExampleJob processactual behavior:
note, only the
ExampleJob sendspan is exportedNotes
I also saw some similar behavior when using
SimpleSpanProcessorwithInMemorySpanExporterwhich also uses mutexes to lock resources 🤔 ... perhaps this bug related to thread/multiprocess safety. resque does some strange things with concurrency in its workers. at least in the version above, the default is to fork for every job.setting FORK_PER_JOB=false in the env actually gives the correct result, but that's not generally desirable, as forking gives you better job isolation, protecting against memory space pollution.
The same behavior happens in the latest version of resque as of this writing (v2.6.0).
I wasn't able to test on ruby 3 due to my local environment being a little borked, but we are stuck on ruby 2.7.6 for now anyway.
Would be nice for this to Just Work out of the box, but in the mean time, if there is a workaround to allow this to export correctly even with FORK_PER_JOB enabled, that would be appreciated.
Thanks!