Skip to content

Commit 989da17

Browse files
perf: Reduce Context Allocations in ActiveJob (#1018)
* perf: Reduce Context Allocations in ActiveJob After seeing changes to Rack::Events that reduced the number of active contexts required for ingress spans, I decided to apply the same changes to ActiveJob * doc: Update default.rb Co-authored-by: Steven Harman <[email protected]> --------- Co-authored-by: Steven Harman <[email protected]>
1 parent a324938 commit 989da17

File tree

4 files changed

+15
-17
lines changed

4 files changed

+15
-17
lines changed

instrumentation/active_job/Appraisals

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@
99
gem 'activejob', "~> #{version}"
1010
end
1111
end
12+
13+
appraise 'activejob-latest' do
14+
gem 'activejob'
15+
end

instrumentation/active_job/lib/opentelemetry/instrumentation/active_job/handlers/default.rb

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ def start(name, id, payload)
4141
# @return [Hash] with the span and generated context tokens
4242
def start_span(name, _id, payload)
4343
span = tracer.start_span(name, attributes: @mapper.call(payload))
44-
tokens = [OpenTelemetry::Context.attach(OpenTelemetry::Trace.context_with_span(span))]
44+
token = OpenTelemetry::Context.attach(OpenTelemetry::Trace.context_with_span(span))
4545

46-
{ span: span, ctx_tokens: tokens }
46+
{ span: span, ctx_token: token }
4747
end
4848

4949
# Creates a span and registers it with the current context
@@ -55,20 +55,20 @@ def start_span(name, _id, payload)
5555
def finish(_name, _id, payload)
5656
otel = payload.delete(:__otel)
5757
span = otel&.fetch(:span)
58-
tokens = otel&.fetch(:ctx_tokens)
58+
token = otel&.fetch(:ctx_token)
5959

6060
on_exception((payload[:error] || payload[:exception_object]), span)
6161
rescue StandardError => e
6262
OpenTelemetry.handle_error(exception: e)
6363
ensure
64-
finish_span(span, tokens)
64+
finish_span(span, token)
6565
end
6666

6767
# Finishes the provided spans and also detaches the associated contexts
6868
#
6969
# @param span [OpenTelemetry::Trace::Span]
70-
# @param tokens [Array] to unregister
71-
def finish_span(span, tokens)
70+
# @param token [Numeric] to unregister
71+
def finish_span(span, token)
7272
# closes the span after all attributes have been finalized
7373
begin
7474
if span&.recording?
@@ -79,8 +79,7 @@ def finish_span(span, tokens)
7979
OpenTelemetry.handle_error(exception: e)
8080
end
8181

82-
# pops the context stack
83-
tokens&.reverse_each do |token|
82+
begin
8483
OpenTelemetry::Context.detach(token)
8584
rescue StandardError => e
8685
OpenTelemetry.handle_error(exception: e)

instrumentation/active_job/lib/opentelemetry/instrumentation/active_job/handlers/enqueue.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ def initialize(...)
2929
def start_span(name, _id, payload)
3030
job = payload.fetch(:job)
3131
span = tracer.start_span(@span_name_formatter.call(job), kind: :producer, attributes: @mapper.call(payload))
32-
tokens = [OpenTelemetry::Context.attach(OpenTelemetry::Trace.context_with_span(span))]
3332
OpenTelemetry.propagation.inject(job.__otel_headers) # This must be transmitted over the wire
34-
{ span: span, ctx_tokens: tokens }
33+
{ span: span, ctx_token: OpenTelemetry::Context.attach(OpenTelemetry::Trace.context_with_span(span)) }
3534
end
3635
end
3736
end

instrumentation/active_job/lib/opentelemetry/instrumentation/active_job/handlers/perform.rb

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ def initialize(...)
2727
# @param payload [Hash] containing job run information
2828
# @return [Hash] with the span and generated context tokens
2929
def start_span(name, _id, payload)
30-
tokens = []
3130
job = payload.fetch(:job)
3231
parent_context = OpenTelemetry.propagation.extract(job.__otel_headers)
3332

@@ -36,30 +35,27 @@ def start_span(name, _id, payload)
3635
# TODO: Refactor into a propagation strategy
3736
propagation_style = @config[:propagation_style]
3837
if propagation_style == :child
39-
tokens << OpenTelemetry::Context.attach(parent_context)
4038
span = tracer.start_span(span_name, kind: :consumer, attributes: @mapper.call(payload))
4139
else
4240
span_context = OpenTelemetry::Trace.current_span(parent_context).context
4341
links = [OpenTelemetry::Trace::Link.new(span_context)] if span_context.valid? && propagation_style == :link
4442
span = tracer.start_root_span(span_name, kind: :consumer, attributes: @mapper.call(payload), links: links)
4543
end
4644

47-
tokens.concat(attach_consumer_context(span))
48-
49-
{ span: span, ctx_tokens: tokens }
45+
{ span: span, ctx_token: attach_consumer_context(span) }
5046
end
5147

5248
# This method attaches a span to multiple contexts:
5349
# 1. Registers the ingress span as the top level ActiveJob span.
5450
# This is used later to enrich the ingress span in children, e.g. setting span status to error when a child event like `discard` terminates due to an error
5551
# 2. Registers the ingress span as the "active" span, which is the default behavior of the SDK.
5652
# @param span [OpenTelemetry::Trace::Span] the currently active span used to record the exception and set the status
57-
# @return [Array] Context tokens that must be detached when finished
53+
# @return [Numeric] Context token that must be detached when finished
5854
def attach_consumer_context(span)
5955
consumer_context = OpenTelemetry::Trace.context_with_span(span)
6056
internal_context = OpenTelemetry::Instrumentation::ActiveJob.context_with_span(span, parent_context: consumer_context)
6157

62-
[consumer_context, internal_context].map { |context| OpenTelemetry::Context.attach(context) }
58+
OpenTelemetry::Context.attach(internal_context)
6359
end
6460
end
6561
end

0 commit comments

Comments
 (0)