From 43002b7a1d80f93e12d15cbd6e39240d7964243e Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Sun, 7 Jun 2026 17:21:33 +0000 Subject: [PATCH 1/5] 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 #2963 Co-Authored-By: junior Co-authored-by: neel --- .../lib/sentry/transport/http_transport.rb | 22 ++++++++++- .../sentry/transport/http_transport_spec.rb | 38 +++++++++++++++++++ sentry-yabeda/lib/sentry/yabeda/adapter.rb | 12 ++++++ .../spec/sentry/yabeda/adapter_spec.rb | 33 ++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) diff --git a/sentry-ruby/lib/sentry/transport/http_transport.rb b/sentry-ruby/lib/sentry/transport/http_transport.rb index 88902557f..dd350491e 100644 --- a/sentry-ruby/lib/sentry/transport/http_transport.rb +++ b/sentry-ruby/lib/sentry/transport/http_transport.rb @@ -23,6 +23,19 @@ class HTTPTransport < Transport Zlib::BufError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED ].freeze + # Thread-local key used to signal that the current thread is actively + # sending a Sentry envelope via HTTP. Consulted by integrations (e.g. + # sentry-yabeda) to prevent re-entrant metric collection that would + # deadlock MetricEventBuffer's non-reentrant mutex. + SENTRY_SENDING_KEY = :sentry_http_transport_sending + private_constant :SENTRY_SENDING_KEY + + # Returns true when the current thread is inside #send_data. + # @api private + def self.sending? + !!Thread.current[SENTRY_SENDING_KEY] + end + def initialize(*args) super log_debug("Sentry HTTP Transport will connect to #{@dsn.server}") if @dsn @@ -45,7 +58,7 @@ def send_data(data) auth_header = generate_auth_header headers["X-Sentry-Auth"] = auth_header if auth_header - response = do_request(endpoint, headers, data) + response = with_sentry_sending { do_request(endpoint, headers, data) } if response.code.match?(/\A2\d{2}/) handle_rate_limited_response(response) if has_rate_limited_header?(response) @@ -113,6 +126,13 @@ def do_request(endpoint, headers, body) private + def with_sentry_sending + Thread.current[SENTRY_SENDING_KEY] = true + yield + ensure + Thread.current[SENTRY_SENDING_KEY] = false + end + def has_rate_limited_header?(headers) headers[RETRY_AFTER_HEADER] || headers[RATE_LIMIT_HEADER] end diff --git a/sentry-ruby/spec/sentry/transport/http_transport_spec.rb b/sentry-ruby/spec/sentry/transport/http_transport_spec.rb index bd722d28f..36acd6edb 100644 --- a/sentry-ruby/spec/sentry/transport/http_transport_spec.rb +++ b/sentry-ruby/spec/sentry/transport/http_transport_spec.rb @@ -432,4 +432,42 @@ expect(subject.conn.use_ssl?).to eq(false) end end + + describe ".sending?" do + it "returns false outside of send_data" do + expect(described_class.sending?).to eq(false) + end + + it "returns true while send_data is executing the HTTP request" do + observed = nil + + allow(subject).to receive(:do_request) do + observed = described_class.sending? + instance_double(Net::HTTPResponse, code: "200", body: "", []: nil, + to_hash: {}) + end + + subject.send_data("data") + + expect(observed).to eq(true) + end + + it "resets to false after send_data completes" do + allow(subject).to receive(:do_request).and_return( + instance_double(Net::HTTPResponse, code: "200", body: "", []: nil, to_hash: {}) + ) + + subject.send_data("data") + + expect(described_class.sending?).to eq(false) + end + + it "resets to false even when send_data raises" do + allow(subject).to receive(:do_request).and_raise(SocketError, "connection failed") + + expect { subject.send_data("data") }.to raise_error(Sentry::ExternalError) + + expect(described_class.sending?).to eq(false) + end + end end diff --git a/sentry-yabeda/lib/sentry/yabeda/adapter.rb b/sentry-yabeda/lib/sentry/yabeda/adapter.rb index fb67308d6..f36514d47 100644 --- a/sentry-yabeda/lib/sentry/yabeda/adapter.rb +++ b/sentry-yabeda/lib/sentry/yabeda/adapter.rb @@ -13,6 +13,7 @@ def register_summary!(_metric); end def perform_counter_increment!(counter, tags, increment) return unless enabled? + return if sentry_http_sending? Sentry.metrics.count( metric_name(counter), @@ -23,6 +24,7 @@ def perform_counter_increment!(counter, tags, increment) def perform_gauge_set!(gauge, tags, value) return unless enabled? + return if sentry_http_sending? Sentry.metrics.gauge( metric_name(gauge), @@ -34,6 +36,7 @@ def perform_gauge_set!(gauge, tags, value) def perform_histogram_measure!(histogram, tags, value) return unless enabled? + return if sentry_http_sending? Sentry.metrics.distribution( metric_name(histogram), @@ -45,6 +48,7 @@ def perform_histogram_measure!(histogram, tags, value) def perform_summary_observe!(summary, tags, value) return unless enabled? + return if sentry_http_sending? Sentry.metrics.distribution( metric_name(summary), @@ -60,6 +64,14 @@ def enabled? Sentry.initialized? && Sentry.configuration.enable_metrics end + # Returns true when the current thread is inside HTTPTransport#send_data. + # Skipping metric forwarding in this case prevents a re-entrant + # MetricEventBuffer mutex acquisition that would raise + # ThreadError: deadlock; recursive locking. + def sentry_http_sending? + Sentry::HTTPTransport.sending? + end + def attributes_for(tags) tags.empty? ? nil : tags end diff --git a/sentry-yabeda/spec/sentry/yabeda/adapter_spec.rb b/sentry-yabeda/spec/sentry/yabeda/adapter_spec.rb index ea7b02890..8699c8796 100644 --- a/sentry-yabeda/spec/sentry/yabeda/adapter_spec.rb +++ b/sentry-yabeda/spec/sentry/yabeda/adapter_spec.rb @@ -234,6 +234,39 @@ def build_metric(type, name:, group: nil, unit: nil) summary = build_metric(:summary, name: :response_size) adapter.perform_summary_observe!(summary, {}, 100) end + + context "when Sentry is actively sending an HTTP request" do + it "does not forward any metrics to avoid deadlocking MetricEventBuffer" do + perform_basic_setup + + allow(Sentry::HTTPTransport).to receive(:sending?).and_return(true) + + expect(Sentry.metrics).not_to receive(:count) + expect(Sentry.metrics).not_to receive(:gauge) + expect(Sentry.metrics).not_to receive(:distribution) + + counter = build_metric(:counter, name: :requests) + gauge = build_metric(:gauge, name: :queue_depth) + histogram = build_metric(:histogram, name: :duration) + summary = build_metric(:summary, name: :response_size) + + adapter.perform_counter_increment!(counter, {}, 1) + adapter.perform_gauge_set!(gauge, {}, 42) + adapter.perform_histogram_measure!(histogram, {}, 100.0) + adapter.perform_summary_observe!(summary, {}, 50) + end + + it "resumes forwarding metrics once the HTTP send is complete" do + perform_basic_setup + + allow(Sentry::HTTPTransport).to receive(:sending?).and_return(false) + + counter = build_metric(:counter, name: :requests, group: :rails) + expect(Sentry.metrics).to receive(:count).with("rails.requests", value: 1, attributes: nil) + + adapter.perform_counter_increment!(counter, {}, 1) + end + end end describe "tag passthrough" do From 90265618ccc4bfc49b398a3ac751388201178b5d Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 08:04:55 +0000 Subject: [PATCH 2/5] 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/sentry-ruby#2963 Refs getsentry/repro#46 Co-Authored-By: junior Co-authored-by: johdax --- .../lib/sentry/telemetry_event_buffer.rb | 7 ++++ ...ed_examples_for_telemetry_event_buffers.rb | 41 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/sentry-ruby/lib/sentry/telemetry_event_buffer.rb b/sentry-ruby/lib/sentry/telemetry_event_buffer.rb index 8c5ce0781..e14df63fb 100644 --- a/sentry-ruby/lib/sentry/telemetry_event_buffer.rb +++ b/sentry-ruby/lib/sentry/telemetry_event_buffer.rb @@ -50,6 +50,13 @@ def flush alias_method :run, :flush def add_item(item) + # Guard against re-entrant locking on the same thread. + # This can occur when transport instrumentation (e.g. Yabeda HTTP metrics, + # custom transport callbacks) calls Sentry.metrics.* from inside send_items, + # which already holds @mutex. Ruby's Mutex is not re-entrant and would raise + # ThreadError: deadlock; recursive locking. Silently drop the item instead. + return self if @mutex.owned? + @mutex.synchronize do return unless ensure_thread diff --git a/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb b/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb index 88eeeba0e..f74881a1a 100644 --- a/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb +++ b/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb @@ -122,6 +122,47 @@ end end + describe "re-entrancy protection" do + let(:max_items) { 3 } + + it "does not deadlock when add_item is called re-entrantly from send_items" do + # Simulates the pattern where transport instrumentation (e.g. yabeda-http_requests, + # a custom transport) calls Sentry.metrics.* during transport#send_data, which + # re-enters add_item on the same thread that already holds @mutex inside send_items. + # + # Without the @mutex.owned? guard this raises: + # ThreadError: deadlock; recursive locking + reentrant_calls = 0 + + allow(client).to receive(:send_envelope) do + reentrant_calls += 1 + # Simulate instrumentation calling back into the buffer mid-send + subject.add_item(event) + # also simulate a second re-entrant call to be sure + subject.add_item(event) + end + + expect { + 3.times { subject.add_item(event) } + }.not_to raise_error + + expect(reentrant_calls).to be >= 1 + end + + it "silently drops the re-entrant item rather than raising" do + items_sent = [] + + allow(client).to receive(:send_envelope) do |envelope| + items_sent << :sent + subject.add_item(event) # re-entrant; must be dropped, not raise + end + + 3.times { subject.add_item(event) } + + expect(items_sent).not_to be_empty + end + end + describe "error handling" do let(:max_items) { 3 } From 3488b969895b7b16b46276fee336e51ca6f250dd Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 08:14:42 +0000 Subject: [PATCH 3/5] 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 Co-authored-by: johdax --- sentry-ruby/spec/sentry/transport/http_transport_spec.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sentry-ruby/spec/sentry/transport/http_transport_spec.rb b/sentry-ruby/spec/sentry/transport/http_transport_spec.rb index 36acd6edb..f8ade275e 100644 --- a/sentry-ruby/spec/sentry/transport/http_transport_spec.rb +++ b/sentry-ruby/spec/sentry/transport/http_transport_spec.rb @@ -443,8 +443,7 @@ allow(subject).to receive(:do_request) do observed = described_class.sending? - instance_double(Net::HTTPResponse, code: "200", body: "", []: nil, - to_hash: {}) + build_fake_response("200") end subject.send_data("data") @@ -453,9 +452,7 @@ end it "resets to false after send_data completes" do - allow(subject).to receive(:do_request).and_return( - instance_double(Net::HTTPResponse, code: "200", body: "", []: nil, to_hash: {}) - ) + allow(subject).to receive(:do_request).and_return(build_fake_response("200")) subject.send_data("data") From 5a51e85f7958c6953154ade1847fd27f2cdc177d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Daxb=C3=B6ck?= Date: Mon, 8 Jun 2026 10:25:13 +0200 Subject: [PATCH 4/5] 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 --- .../lib/sentry/telemetry_event_buffer.rb | 8 ++--- .../lib/sentry/transport/http_transport.rb | 22 +----------- .../sentry/transport/http_transport_spec.rb | 35 ------------------- ...ed_examples_for_telemetry_event_buffers.rb | 6 ---- sentry-yabeda/lib/sentry/yabeda/adapter.rb | 12 ------- .../spec/sentry/yabeda/adapter_spec.rb | 33 ----------------- 6 files changed, 4 insertions(+), 112 deletions(-) diff --git a/sentry-ruby/lib/sentry/telemetry_event_buffer.rb b/sentry-ruby/lib/sentry/telemetry_event_buffer.rb index e14df63fb..77e23058c 100644 --- a/sentry-ruby/lib/sentry/telemetry_event_buffer.rb +++ b/sentry-ruby/lib/sentry/telemetry_event_buffer.rb @@ -37,6 +37,8 @@ def initialize(configuration, client, event_class:, max_items:, max_items_before end def flush + return self if @mutex.owned? + @mutex.synchronize do return if empty? @@ -50,11 +52,7 @@ def flush alias_method :run, :flush def add_item(item) - # Guard against re-entrant locking on the same thread. - # This can occur when transport instrumentation (e.g. Yabeda HTTP metrics, - # custom transport callbacks) calls Sentry.metrics.* from inside send_items, - # which already holds @mutex. Ruby's Mutex is not re-entrant and would raise - # ThreadError: deadlock; recursive locking. Silently drop the item instead. + # Prevent ThreadError from re-entrant locking (e.g. transport instrumentation calling Sentry.metrics.*) return self if @mutex.owned? @mutex.synchronize do diff --git a/sentry-ruby/lib/sentry/transport/http_transport.rb b/sentry-ruby/lib/sentry/transport/http_transport.rb index dd350491e..88902557f 100644 --- a/sentry-ruby/lib/sentry/transport/http_transport.rb +++ b/sentry-ruby/lib/sentry/transport/http_transport.rb @@ -23,19 +23,6 @@ class HTTPTransport < Transport Zlib::BufError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED ].freeze - # Thread-local key used to signal that the current thread is actively - # sending a Sentry envelope via HTTP. Consulted by integrations (e.g. - # sentry-yabeda) to prevent re-entrant metric collection that would - # deadlock MetricEventBuffer's non-reentrant mutex. - SENTRY_SENDING_KEY = :sentry_http_transport_sending - private_constant :SENTRY_SENDING_KEY - - # Returns true when the current thread is inside #send_data. - # @api private - def self.sending? - !!Thread.current[SENTRY_SENDING_KEY] - end - def initialize(*args) super log_debug("Sentry HTTP Transport will connect to #{@dsn.server}") if @dsn @@ -58,7 +45,7 @@ def send_data(data) auth_header = generate_auth_header headers["X-Sentry-Auth"] = auth_header if auth_header - response = with_sentry_sending { do_request(endpoint, headers, data) } + response = do_request(endpoint, headers, data) if response.code.match?(/\A2\d{2}/) handle_rate_limited_response(response) if has_rate_limited_header?(response) @@ -126,13 +113,6 @@ def do_request(endpoint, headers, body) private - def with_sentry_sending - Thread.current[SENTRY_SENDING_KEY] = true - yield - ensure - Thread.current[SENTRY_SENDING_KEY] = false - end - def has_rate_limited_header?(headers) headers[RETRY_AFTER_HEADER] || headers[RATE_LIMIT_HEADER] end diff --git a/sentry-ruby/spec/sentry/transport/http_transport_spec.rb b/sentry-ruby/spec/sentry/transport/http_transport_spec.rb index f8ade275e..bd722d28f 100644 --- a/sentry-ruby/spec/sentry/transport/http_transport_spec.rb +++ b/sentry-ruby/spec/sentry/transport/http_transport_spec.rb @@ -432,39 +432,4 @@ expect(subject.conn.use_ssl?).to eq(false) end end - - describe ".sending?" do - it "returns false outside of send_data" do - expect(described_class.sending?).to eq(false) - end - - it "returns true while send_data is executing the HTTP request" do - observed = nil - - allow(subject).to receive(:do_request) do - observed = described_class.sending? - build_fake_response("200") - end - - subject.send_data("data") - - expect(observed).to eq(true) - end - - it "resets to false after send_data completes" do - allow(subject).to receive(:do_request).and_return(build_fake_response("200")) - - subject.send_data("data") - - expect(described_class.sending?).to eq(false) - end - - it "resets to false even when send_data raises" do - allow(subject).to receive(:do_request).and_raise(SocketError, "connection failed") - - expect { subject.send_data("data") }.to raise_error(Sentry::ExternalError) - - expect(described_class.sending?).to eq(false) - end - end end diff --git a/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb b/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb index f74881a1a..05e4e7e24 100644 --- a/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb +++ b/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb @@ -126,12 +126,6 @@ let(:max_items) { 3 } it "does not deadlock when add_item is called re-entrantly from send_items" do - # Simulates the pattern where transport instrumentation (e.g. yabeda-http_requests, - # a custom transport) calls Sentry.metrics.* during transport#send_data, which - # re-enters add_item on the same thread that already holds @mutex inside send_items. - # - # Without the @mutex.owned? guard this raises: - # ThreadError: deadlock; recursive locking reentrant_calls = 0 allow(client).to receive(:send_envelope) do diff --git a/sentry-yabeda/lib/sentry/yabeda/adapter.rb b/sentry-yabeda/lib/sentry/yabeda/adapter.rb index f36514d47..fb67308d6 100644 --- a/sentry-yabeda/lib/sentry/yabeda/adapter.rb +++ b/sentry-yabeda/lib/sentry/yabeda/adapter.rb @@ -13,7 +13,6 @@ def register_summary!(_metric); end def perform_counter_increment!(counter, tags, increment) return unless enabled? - return if sentry_http_sending? Sentry.metrics.count( metric_name(counter), @@ -24,7 +23,6 @@ def perform_counter_increment!(counter, tags, increment) def perform_gauge_set!(gauge, tags, value) return unless enabled? - return if sentry_http_sending? Sentry.metrics.gauge( metric_name(gauge), @@ -36,7 +34,6 @@ def perform_gauge_set!(gauge, tags, value) def perform_histogram_measure!(histogram, tags, value) return unless enabled? - return if sentry_http_sending? Sentry.metrics.distribution( metric_name(histogram), @@ -48,7 +45,6 @@ def perform_histogram_measure!(histogram, tags, value) def perform_summary_observe!(summary, tags, value) return unless enabled? - return if sentry_http_sending? Sentry.metrics.distribution( metric_name(summary), @@ -64,14 +60,6 @@ def enabled? Sentry.initialized? && Sentry.configuration.enable_metrics end - # Returns true when the current thread is inside HTTPTransport#send_data. - # Skipping metric forwarding in this case prevents a re-entrant - # MetricEventBuffer mutex acquisition that would raise - # ThreadError: deadlock; recursive locking. - def sentry_http_sending? - Sentry::HTTPTransport.sending? - end - def attributes_for(tags) tags.empty? ? nil : tags end diff --git a/sentry-yabeda/spec/sentry/yabeda/adapter_spec.rb b/sentry-yabeda/spec/sentry/yabeda/adapter_spec.rb index 8699c8796..ea7b02890 100644 --- a/sentry-yabeda/spec/sentry/yabeda/adapter_spec.rb +++ b/sentry-yabeda/spec/sentry/yabeda/adapter_spec.rb @@ -234,39 +234,6 @@ def build_metric(type, name:, group: nil, unit: nil) summary = build_metric(:summary, name: :response_size) adapter.perform_summary_observe!(summary, {}, 100) end - - context "when Sentry is actively sending an HTTP request" do - it "does not forward any metrics to avoid deadlocking MetricEventBuffer" do - perform_basic_setup - - allow(Sentry::HTTPTransport).to receive(:sending?).and_return(true) - - expect(Sentry.metrics).not_to receive(:count) - expect(Sentry.metrics).not_to receive(:gauge) - expect(Sentry.metrics).not_to receive(:distribution) - - counter = build_metric(:counter, name: :requests) - gauge = build_metric(:gauge, name: :queue_depth) - histogram = build_metric(:histogram, name: :duration) - summary = build_metric(:summary, name: :response_size) - - adapter.perform_counter_increment!(counter, {}, 1) - adapter.perform_gauge_set!(gauge, {}, 42) - adapter.perform_histogram_measure!(histogram, {}, 100.0) - adapter.perform_summary_observe!(summary, {}, 50) - end - - it "resumes forwarding metrics once the HTTP send is complete" do - perform_basic_setup - - allow(Sentry::HTTPTransport).to receive(:sending?).and_return(false) - - counter = build_metric(:counter, name: :requests, group: :rails) - expect(Sentry.metrics).to receive(:count).with("rails.requests", value: 1, attributes: nil) - - adapter.perform_counter_increment!(counter, {}, 1) - end - end end describe "tag passthrough" do From 8dba3c0ada801f8de35c0a67edd75bcfbc443a3a Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Mon, 8 Jun 2026 14:52:22 +0200 Subject: [PATCH 5/5] only guard add_item --- sentry-ruby/lib/sentry/telemetry_event_buffer.rb | 2 -- .../spec/support/shared_examples_for_telemetry_event_buffers.rb | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sentry-ruby/lib/sentry/telemetry_event_buffer.rb b/sentry-ruby/lib/sentry/telemetry_event_buffer.rb index 77e23058c..1ced8e360 100644 --- a/sentry-ruby/lib/sentry/telemetry_event_buffer.rb +++ b/sentry-ruby/lib/sentry/telemetry_event_buffer.rb @@ -37,8 +37,6 @@ def initialize(configuration, client, event_class:, max_items:, max_items_before end def flush - return self if @mutex.owned? - @mutex.synchronize do return if empty? diff --git a/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb b/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb index 05e4e7e24..576779723 100644 --- a/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb +++ b/sentry-ruby/spec/support/shared_examples_for_telemetry_event_buffers.rb @@ -154,6 +154,7 @@ 3.times { subject.add_item(event) } expect(items_sent).not_to be_empty + expect(string_io.string).not_to include("deadlock") end end