Skip to content

Reproduction for sentry-ruby#2963#46

Merged
sl0thentr0py merged 4 commits into
mainfrom
repro/sentry-ruby-2963
Jun 8, 2026
Merged

Reproduction for sentry-ruby#2963#46
sl0thentr0py merged 4 commits into
mainfrom
repro/sentry-ruby-2963

Conversation

@dingsdax

@dingsdax dingsdax commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Reference

getsentry/sentry-ruby#2963

Description

Reproduces the deadlock (deadlock; recursive locking) and memory leak when both enable_logs and enable_metrics are enabled in sentry-ruby 6.6.0.

The root cause is a re-entrancy bug in TelemetryEventBuffer: when HTTP instrumentation (e.g. Yabeda) calls Sentry.metrics.* during transport.send_data, it re-enters MetricEventBuffer#add_item while the flush already holds the non-reentrant Mutex.

How to run

cd sentry-ruby/2963
bundle install
bundle exec ruby repro.rb

🤖 Generated with Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d797a29. Configure here.

Comment thread sentry-ruby/2963/repro.rb Outdated
Comment thread sentry-ruby/2963/repro.rb Outdated
sentry-junior Bot added a commit to getsentry/sentry-ruby that referenced this pull request Jun 8, 2026
Any transport instrumentation that calls Sentry.metrics.* from within
transport#send_data re-enters MetricEventBuffer#add_item on the same
thread that already holds @Mutex inside send_items, raising:

  ThreadError: deadlock; recursive locking

This includes any non-Yabeda code such as custom transports or other
HTTP instrumentation (see getsentry/repro#46 for a minimal repro using
a ReentrantTransport that calls Sentry.metrics directly from send_data).

Fix: check @mutex.owned? before acquiring in add_item and silently drop
the item when the current thread already holds the lock. This is the
defensive fix at the correct abstraction level and protects all buffers
(MetricEventBuffer, LogEventBuffer) from all callers.

The sentry-yabeda Adapter guard (HTTPTransport.sending?) is retained as
defense-in-depth and avoids constructing a MetricEvent that will be
dropped, but the buffer-level fix is the load-bearing protection.

Refs #2963
Refs getsentry/repro#46

Co-Authored-By: junior <[email protected]>

Co-authored-by: johdax <[email protected]>
@sl0thentr0py
sl0thentr0py merged commit f255537 into main Jun 8, 2026
12 of 13 checks passed
@sl0thentr0py
sl0thentr0py deleted the repro/sentry-ruby-2963 branch June 8, 2026 13:09
iloveitaly pushed a commit to iloveitaly/raven-ruby that referenced this pull request Jun 9, 2026
…etsentry#2964)

* fix(yabeda): skip Sentry adapter during own HTTP transport sends

MetricEventBuffer uses a non-reentrant Mutex. When HTTP instrumentation
(e.g. yabeda-http_requests, yabeda-rails) observes the outgoing Net::HTTP
call made by HTTPTransport#send_data, it triggers Sentry::Yabeda::Adapter
which calls Sentry.metrics.* -> MetricEventBuffer#add_item on the same
thread that already holds the buffer mutex, raising:

  ThreadError: deadlock; recursive locking

Fix: set a thread-local flag (SENTRY_SENDING_KEY) in HTTPTransport for
the duration of the do_request call and check it in all Adapter#perform_*!
methods, silently skipping metric forwarding for Sentry's own HTTP sends.

Fixes getsentry#2963

Co-Authored-By: junior <[email protected]>

Co-authored-by: neel <[email protected]>

* fix(telemetry): guard add_item against re-entrant mutex acquisition

Any transport instrumentation that calls Sentry.metrics.* from within
transport#send_data re-enters MetricEventBuffer#add_item on the same
thread that already holds @Mutex inside send_items, raising:

  ThreadError: deadlock; recursive locking

This includes any non-Yabeda code such as custom transports or other
HTTP instrumentation (see getsentry/repro#46 for a minimal repro using
a ReentrantTransport that calls Sentry.metrics directly from send_data).

Fix: check @mutex.owned? before acquiring in add_item and silently drop
the item when the current thread already holds the lock. This is the
defensive fix at the correct abstraction level and protects all buffers
(MetricEventBuffer, LogEventBuffer) from all callers.

The sentry-yabeda Adapter guard (HTTPTransport.sending?) is retained as
defense-in-depth and avoids constructing a MetricEvent that will be
dropped, but the buffer-level fix is the load-bearing protection.

Refs getsentry#2963
Refs getsentry/repro#46

Co-Authored-By: junior <[email protected]>

Co-authored-by: johdax <[email protected]>

* test(transport): fix rubocop syntax error in .sending? specs

Replace instance_double with []: nil keyword syntax (invalid in Ruby 2.7
parser) with build_fake_response, consistent with the rest of the spec.

Co-Authored-By: junior <[email protected]>

Co-authored-by: johdax <[email protected]>

* refactor(telemetry): simplify re-entrant deadlock fix to single mutex guard

Remove the thread-local flag layer (HTTPTransport.sending?, with_sentry_sending,
Yabeda adapter sentry_http_sending? checks) in favor of the @mutex.owned? guard
in TelemetryEventBuffer alone. This is simpler, more general, and eliminates
cross-gem coupling. Also add the guard to flush which had the same exposure.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* only guard add_item

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: neel <[email protected]>
Co-authored-by: johdax <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
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.

2 participants