Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ module Dup
class EventHandler
include ::Rack::Events::Abstract

OTEL_TOKEN_AND_SPAN = 'otel.rack.token_and_span'
OTEL_CONTEXT_INFO = 'otel.context_info'
EMPTY_HASH = {}.freeze

# Creates a server span for this current request using the incoming parent context
Expand All @@ -62,7 +62,8 @@ def on_start(request, _)
span = create_span(parent_context, request)
span_ctx = OpenTelemetry::Trace.context_with_span(span, parent_context: parent_context)
rack_ctx = OpenTelemetry::Instrumentation::Rack.context_with_span(span, parent_context: span_ctx)
request.env[OTEL_TOKEN_AND_SPAN] = [OpenTelemetry::Context.attach(rack_ctx), span]
token = OpenTelemetry::Context.attach(rack_ctx)
request.env[OTEL_CONTEXT_INFO] = [Fiber.current, token, span]
rescue StandardError => e
OpenTelemetry.handle_error(exception: e)
end
Expand Down Expand Up @@ -209,11 +210,17 @@ def request_span_attributes(env)
end

def detach_context(request)
return nil unless request.env[OTEL_TOKEN_AND_SPAN]
otel_context_info = request.env[OTEL_CONTEXT_INFO]
return unless otel_context_info

token, span = request.env[OTEL_TOKEN_AND_SPAN]
original_fiber, token, span = otel_context_info
span.finish
OpenTelemetry::Context.detach(token)

if Fiber.current.equal?(original_fiber)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you share more details about how this could happen?

What application server are you using that invokes the rack events in different fibers?

OpenTelemetry::Context.detach(token)
else
OpenTelemetry.logger.debug { '[rack] Skipping context detach: response processed in different fiber than request.' }
end
rescue StandardError => e
OpenTelemetry.handle_error(exception: e)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ module Old
class EventHandler
include ::Rack::Events::Abstract

OTEL_TOKEN_AND_SPAN = 'otel.rack.token_and_span'
OTEL_CONTEXT_INFO = 'otel.context_info'
EMPTY_HASH = {}.freeze

# Creates a server span for this current request using the incoming parent context
Expand All @@ -62,7 +62,8 @@ def on_start(request, _)
span = create_span(parent_context, request)
span_ctx = OpenTelemetry::Trace.context_with_span(span, parent_context: parent_context)
rack_ctx = OpenTelemetry::Instrumentation::Rack.context_with_span(span, parent_context: span_ctx)
request.env[OTEL_TOKEN_AND_SPAN] = [OpenTelemetry::Context.attach(rack_ctx), span]
token = OpenTelemetry::Context.attach(rack_ctx)
request.env[OTEL_CONTEXT_INFO] = [Fiber.current, token, span]
rescue StandardError => e
OpenTelemetry.handle_error(exception: e)
end
Expand Down Expand Up @@ -198,11 +199,17 @@ def request_span_attributes(env)
end

def detach_context(request)
return nil unless request.env[OTEL_TOKEN_AND_SPAN]
otel_context_info = request.env[OTEL_CONTEXT_INFO]
return unless otel_context_info

token, span = request.env[OTEL_TOKEN_AND_SPAN]
original_fiber, token, span = otel_context_info
span.finish
OpenTelemetry::Context.detach(token)

if Fiber.current.equal?(original_fiber)
OpenTelemetry::Context.detach(token)
else
OpenTelemetry.logger.debug { '[rack] Skipping context detach: response processed in different fiber than request.' }
end
rescue StandardError => e
OpenTelemetry.handle_error(exception: e)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ module Stable
class EventHandler
include ::Rack::Events::Abstract

OTEL_TOKEN_AND_SPAN = 'otel.rack.token_and_span'
OTEL_CONTEXT_INFO = 'otel.context_info'
EMPTY_HASH = {}.freeze

# Creates a server span for this current request using the incoming parent context
Expand All @@ -62,7 +62,8 @@ def on_start(request, _)
span = create_span(parent_context, request)
span_ctx = OpenTelemetry::Trace.context_with_span(span, parent_context: parent_context)
rack_ctx = OpenTelemetry::Instrumentation::Rack.context_with_span(span, parent_context: span_ctx)
request.env[OTEL_TOKEN_AND_SPAN] = [OpenTelemetry::Context.attach(rack_ctx), span]
token = OpenTelemetry::Context.attach(rack_ctx)
request.env[OTEL_CONTEXT_INFO] = [Fiber.current, token, span]
rescue StandardError => e
OpenTelemetry.handle_error(exception: e)
end
Expand Down Expand Up @@ -197,11 +198,17 @@ def request_span_attributes(env)
end

def detach_context(request)
return nil unless request.env[OTEL_TOKEN_AND_SPAN]
otel_context_info = request.env[OTEL_CONTEXT_INFO]
return unless otel_context_info

token, span = request.env[OTEL_TOKEN_AND_SPAN]
original_fiber, token, span = otel_context_info
span.finish
OpenTelemetry::Context.detach(token)

if Fiber.current.equal?(original_fiber)
OpenTelemetry::Context.detach(token)
else
OpenTelemetry.logger.debug { '[rack] Skipping context detach: response processed in different fiber than request.' }
end
rescue StandardError => e
OpenTelemetry.handle_error(exception: e)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -508,4 +508,38 @@ class EventHandlerError < StandardError
_(proxy_event).must_be_nil
end
end

describe 'cross-fiber context detachment' do
it 'finishes span without error when on_finish is called from different fiber' do
request = Rack::MockRequest.env_for('/')
rack_request = Rack::Request.new(request)
response = Rack::Response.new

fiber1 = Fiber.new do
handler.on_start(rack_request, nil)
Fiber.yield
end
fiber1.resume

fiber2 = Fiber.new do
handler.on_finish(rack_request, response)
end
fiber2.resume

_(finished_spans.size).must_equal 1
_(rack_span.name).must_equal 'GET'
end

it 'detaches context normally when same fiber' do
request = Rack::MockRequest.env_for('/')
rack_request = Rack::Request.new(request)
response = Rack::Response.new

handler.on_start(rack_request, nil)
handler.on_finish(rack_request, response)

_(finished_spans.size).must_equal 1
_(rack_span.name).must_equal 'GET'
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -483,4 +483,38 @@ class EventHandlerError < StandardError
_(proxy_event).must_be_nil
end
end

describe 'cross-fiber context detachment' do
it 'finishes span without error when on_finish is called from different fiber' do
request = Rack::MockRequest.env_for('/')
rack_request = Rack::Request.new(request)
response = Rack::Response.new

fiber1 = Fiber.new do
handler.on_start(rack_request, nil)
Fiber.yield
end
fiber1.resume

fiber2 = Fiber.new do
handler.on_finish(rack_request, response)
end
fiber2.resume

_(finished_spans.size).must_equal 1
_(rack_span.name).must_equal 'HTTP GET'
end

it 'detaches context normally when same fiber' do
request = Rack::MockRequest.env_for('/')
rack_request = Rack::Request.new(request)
response = Rack::Response.new

handler.on_start(rack_request, nil)
handler.on_finish(rack_request, response)

_(finished_spans.size).must_equal 1
_(rack_span.name).must_equal 'HTTP GET'
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -479,4 +479,38 @@ class EventHandlerError < StandardError
_(proxy_event).must_be_nil
end
end

describe 'cross-fiber context detachment' do
it 'finishes span without error when on_finish is called from different fiber' do
request = Rack::MockRequest.env_for('/')
rack_request = Rack::Request.new(request)
response = Rack::Response.new

fiber1 = Fiber.new do
handler.on_start(rack_request, nil)
Fiber.yield
end
fiber1.resume

fiber2 = Fiber.new do
handler.on_finish(rack_request, response)
end
fiber2.resume

_(finished_spans.size).must_equal 1
_(rack_span.name).must_equal 'GET'
end

it 'detaches context normally when same fiber' do
request = Rack::MockRequest.env_for('/')
rack_request = Rack::Request.new(request)
response = Rack::Response.new

handler.on_start(rack_request, nil)
handler.on_finish(rack_request, response)

_(finished_spans.size).must_equal 1
_(rack_span.name).must_equal 'GET'
end
end
end
Loading