Skip to content

fix(rack): fiber-safe context detachment in EventHandler - #2130

Open
rsamoilov wants to merge 3 commits into
open-telemetry:mainfrom
rage-rb:fix/rack-fiber-safe-context-detachment
Open

fix(rack): fiber-safe context detachment in EventHandler#2130
rsamoilov wants to merge 3 commits into
open-telemetry:mainfrom
rage-rb:fix/rack-fiber-safe-context-detachment

Conversation

@rsamoilov

Copy link
Copy Markdown

Summary

Fixes context detachment errors in fiber-based environments.

Problem: When using Rack::Events, the on_finish callback can be invoked from a different fiber than on_start (e.g., when streaming response bodies). This causes OpenTelemetry::Context.detach(token) to fail with:

ERROR -- : OpenTelemetry error: calls to detach should match corresponding calls to attach

Solution: Store the fiber reference alongside the token and span. On detach_context:

  • Always finish the span (trace data is never lost)
  • Only detach context if running in the same fiber
  • Log a debug message when detachment is skipped

Note on fiber pooling: Skipping detach could leave stale context in the original fiber. However, servers that reuse fibers across requests would be fundamentally incompatible with any Ruby feature relying on fiber-locals (e.g., ActiveSupport::CurrentAttributes), so this isn't something we should solve at the instrumentation level.

Changes

  • Changed env key from otel.rack.token_and_span to otel.context_info
  • Changed storage format from [token, span] to [fiber, token, span]
  • Updated detach_context to check fiber identity before detaching
  • Applied changes to all three convention variants (stable, old, dup)

Test plan

  • Added unit tests for same-fiber detachment
  • Added unit tests for cross-fiber detachment (verifies span finishes without error)
  • All existing tests pass across all 12 configurations (4 Rack versions × 3 conventions)

@linux-foundation-easycla

linux-foundation-easycla Bot commented Mar 26, 2026

Copy link
Copy Markdown

CLA Signed

The committers listed above are authorized under a signed CLA.

  • ✅ login: rsamoilov / name: Roman Samoilov (1129d8f)

Store fiber reference with context token to safely skip detachment
when on_finish is called from a different fiber than on_start.
Spans are always finished regardless of fiber.
@rsamoilov
rsamoilov force-pushed the fix/rack-fiber-safe-context-detachment branch from 16aa912 to 1129d8f Compare April 13, 2026 08:29
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?

@arielvalentin arielvalentin left a comment

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.

I am concerned about passing around references to fibers and want to better understand if we are leaking context objects or detaching them out of order.

Could you share more information or a small script that demonstrates the problem?

@rsamoilov

Copy link
Copy Markdown
Author

I'm using Rage, which runs each request in a separate fiber. But the issue isn't specific to Rage - it affects any setup where the response body is closed in a different fiber than the one that handled the request.

Here's a minimal reproduction that demonstrates the problem using just Puma + a simple middleware: https://gist.github.com/rsamoilov/c01b3923af0a35dd75b34056062abf0a

WrapInFiber simulates the fiber boundary. Rack::Events calls on_start inside the fiber (where context is attached), but calls on_finish when the body is closed - back in the original fiber. Since OTel context is fiber-local, the detach fails.

The span always finishes regardless of which fiber we're in. The only thing skipped is the Context.detach call when it would error anyway.

@github-actions

Copy link
Copy Markdown
Contributor

👋 This pull request has been marked as stale because it has been open with no activity. You can: comment on the issue or remove the stale label to hold stale off for a while, add the keep label to hold stale off permanently, or do nothing. If you do nothing this pull request will be closed eventually by the stale bot

@github-actions github-actions Bot added stale Marks an issue/PR stale and removed stale Marks an issue/PR stale labels May 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

👋 This pull request has been marked as stale because it has been open with no activity. You can: comment on the issue or remove the stale label to hold stale off for a while, add the keep label to hold stale off permanently, or do nothing. If you do nothing this pull request will be closed eventually by the stale bot

@github-actions github-actions Bot added the stale Marks an issue/PR stale label Jun 19, 2026
@rsamoilov

Copy link
Copy Markdown
Author

Commenting to remove the stale status, but let me know if you're not comfortable with this change and the PR should be closed.

@xuan-cao-swi xuan-cao-swi added the keep Ensures stale-bot keeps this issue/PR open label Jun 19, 2026
@kaylareopelle kaylareopelle removed the stale Marks an issue/PR stale label Jun 27, 2026
@kaylareopelle

Copy link
Copy Markdown
Contributor

Hi @rsamoilov, I'll add this to our next SIG agenda. I hope to have more for you after that meeting.

Now, the request itself will be attached to its context in a hash,
allowing the specific context associated with that hash to be detached
on finish.

This should resolve issues with fiber-based frameworks and Sinatra
environments that mount multiple apps.
@kaylareopelle

kaylareopelle commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Hi @rsamoilov, thanks for your patience while we reviewed your proposal. The PR raises a real concern with fiber-based frameworks that I wasn't aware of. Thank you for the reproduction script. With the growth of Fibers in Ruby, I'd like to see our Rack instrumentation better support them.

After you opened this, we received a bug report from a Sinatra user (#2425) about correctly passing context in a scenario where multiple apps are mounted. I opened #2476 to fix this using a last-in, first-out context stack.

That solution falls short if we're running the code in a fiber context, since fibers can attach and detach out of order.

To resolve both of our issues, I suggest we consider using the Rack request itself as a reference and switching context management to a hash so we can always detach the context specifically associated with the request. This does still require instrumentation for libraries to correctly pass context for this to work.

I made a branch off your work with the proposal. I wanted to draft a PR to merge into this branch, but I can't seem to find your fork. Here's the comparison in the meantime: https://github.com/open-telemetry/opentelemetry-ruby-contrib/compare/main...kaylareopelle:opentelemetry-ruby-contrib:fiber-safe-context-with-multi-app-fix?expand=1

Could you take a look and let me know what you think? I'm also happy to open this as a PR in the main repo if that's easier for you to review.

@rsamoilov

Copy link
Copy Markdown
Author

Hi @kaylareopelle,

One fix to tackle both problems at once - love it 😄

Your approach looks great, and I can confirm the error is fixed with these changes.

I've granted you access to my fork just in case. Will be happy to review your changes either here or in another PR.

@kaylareopelle

Copy link
Copy Markdown
Contributor

Hi @rsamoilov, thank you for the access! I've opened the PR to merge my branch into this one: rage-rb#1

…i-app-fix

fix: Update Rack context management to use hash
@rsamoilov

Copy link
Copy Markdown
Author

Hey @kaylareopelle ,

While looking at your changes, I realised there was another problem my initial fix missed.

I opened rage-rb#2 - could you please give it a look?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

instrumentation-rack instrumentation-sinatra keep Ensures stale-bot keeps this issue/PR open

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants