Skip to content

Conversation

Ayush-developer
Copy link

@Ayush-developer Ayush-developer commented Oct 5, 2025

Thanks for your Pull Request 🎉

Please keep these instructions in mind so we can review it more efficiently:

Other Notes

Description

Describe your changes:

This introduces a custom Sentry integration for the concurrent-ruby library, ensuring proper hub and span propagation across background threads created by Concurrent::Promises.future and Concurrent::Promises::Promise.

Key changes:

Added a Sentry::ConcurrentRuby module that patches:

Concurrent::Promises.future

Concurrent::Promises::Future#on_resolution

Automatically propagates the current Sentry hub and active span to background threads.

Wraps each future/promise execution inside Sentry.with_child_span, creating a child span for async tasks.

Sets the child span as the current span for that thread to preserve the correct trace hierarchy.

Removes the need for manually calling Sentry.with_child_span inside individual futures.

Result:

image

Thread.current.thread_variable_set(Sentry::ConcurrentRuby::SENTRY_SPAN_KEY, current_span)
Sentry.with_child_span(op: "api.request", description: "Concurrent::Promise") do |child_span|
Sentry.get_current_scope.set_span(child_span)
block.call(*args)
Copy link

Choose a reason for hiding this comment

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

Bug: Span Propagation Inconsistency

Span context propagation is incomplete. In patch_promises, current_span is retrieved from the current scope, ignoring the propagated thread variable. Both patch_futures and patch_promises also create a child span but don't restore the original span, which can lead to inconsistent scope state and incorrect span hierarchy.

Fix in Cursor Fix in Web

Comment on lines +41 to +42
current_hub = Thread.current.thread_variable_get(Sentry::ConcurrentRuby::SENTRY_THREAD_KEY) || Sentry.get_current_hub
current_span = Sentry.get_current_scope.get_span
Copy link

Choose a reason for hiding this comment

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

Potential bug: The on_resolution method retrieves current_hub from a thread variable but gets current_span from Sentry.get_current_scope, which may use a different hub, breaking trace context.
  • Description: In the on_resolution method, current_hub is retrieved from either a thread-local variable (SENTRY_THREAD_KEY) or Sentry.get_current_hub. However, current_span is always fetched via Sentry.get_current_scope.get_span, which internally calls Sentry.get_current_hub. If the thread variable contains a hub different from the current thread's hub (e.g., when a promise chain crosses threads), the span will be associated with the wrong hub. This leads to an incorrect span hierarchy and broken tracing information. The implementation also ignores the stored span in the SENTRY_SPAN_KEY thread variable.

  • Suggested fix: Retrieve the span from the same source as the hub. When current_hub is loaded from the thread variable, the corresponding span should also be loaded from its thread variable (SENTRY_SPAN_KEY) to ensure the hub and span contexts are consistent.
    severity: 0.6, confidence: 0.9

Did we get this right? 👍 / 👎 to inform future reviews.

end

# Install after Sentry loaded
Sentry::ConcurrentRuby.install if defined?(Sentry)
Copy link

Choose a reason for hiding this comment

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

Potential bug: The integration accesses ::Concurrent::Promises without checking if the concurrent-ruby gem is loaded, which can cause a NameError on application startup.
  • Description: The Sentry::ConcurrentRuby.install method is called when lib/concurrent_ruby.rb is loaded. This method accesses constants like ::Concurrent::Promises. However, there is no check to ensure the concurrent-ruby gem has been loaded first. If a user includes this file without having concurrent-ruby in their Gemfile, the application will crash with a NameError during initialization. This behavior is inconsistent with other Sentry integrations, which perform a defined?() check on the dependency's constants before attempting to patch them.

  • Suggested fix: Add a check for the presence of the Concurrent::Promises constant before calling Sentry::ConcurrentRuby.install, similar to other integrations. For example: Sentry::ConcurrentRuby.install if defined?(Sentry) && defined?(::Concurrent::Promises).
    severity: 0.8, confidence: 0.95

Did we get this right? 👍 / 👎 to inform future reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant