Skip to content

Commit 3860fa7

Browse files
authored
Merge pull request #3910 from DataDog/tonycthsu/avoid-deadlock
Prevent telemetry deadlock with no-op
2 parents c9994df + f64ae44 commit 3860fa7

File tree

3 files changed

+26
-4
lines changed

3 files changed

+26
-4
lines changed

lib/datadog/core/telemetry/logger.rb

+20-3
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
module Datadog
44
module Core
55
module Telemetry
6-
# === INTRENAL USAGE ONLY ===
6+
# === INTERNAL USAGE ONLY ===
77
#
88
# Report telemetry logs via delegating to the telemetry component instance via mutex.
99
#
1010
# IMPORTANT: Invoking this method during the lifecycle of component initialization will
11-
# cause a non-recoverable deadlock
11+
# be no-op instead.
1212
#
1313
# For developer using this module:
1414
# read: lib/datadog/core/telemetry/logging.rb
@@ -25,7 +25,24 @@ def error(description)
2525
private
2626

2727
def instance
28-
Datadog.send(:components).telemetry
28+
# Component initialization uses a mutex to avoid having concurrent initialization.
29+
# Trying to access the telemetry component during initialization (specifically:
30+
# from the thread that's actually doing the initialization) would cause a deadlock,
31+
# since accessing the components would try to recursively lock the mutex.
32+
#
33+
# To work around this, we use allow_initialization: false to avoid triggering this issue.
34+
#
35+
# The downside is: this leaves us unable to report telemetry during component initialization.
36+
components = Datadog.send(:components, allow_initialization: false)
37+
38+
if components && components.telemetry
39+
components.telemetry
40+
else
41+
Datadog.logger.warn(
42+
'Fail to send telemetry log before components initialization or within components lifecycle'
43+
)
44+
nil
45+
end
2946
end
3047
end
3148
end

lib/datadog/core/telemetry/logging.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
module Datadog
88
module Core
99
module Telemetry
10-
# === INTRENAL USAGE ONLY ===
10+
# === INTERNAL USAGE ONLY ===
1111
#
1212
# Logging interface for sending telemetry logs... so we can fix them.
1313
#

spec/datadog/core/telemetry/logger_spec.rb

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
telemetry = instance_double(Datadog::Core::Telemetry::Component)
1111
allow(Datadog.send(:components)).to receive(:telemetry).and_return(telemetry)
1212
expect(telemetry).to receive(:report).with(exception, level: :error, description: 'Oops...')
13+
expect(Datadog.logger).not_to receive(:warn)
1314

1415
expect do
1516
described_class.report(exception, level: :error, description: 'Oops...')
@@ -22,6 +23,7 @@
2223
telemetry = instance_double(Datadog::Core::Telemetry::Component)
2324
allow(Datadog.send(:components)).to receive(:telemetry).and_return(telemetry)
2425
expect(telemetry).to receive(:report).with(exception, level: :error, description: nil)
26+
expect(Datadog.logger).not_to receive(:warn)
2527

2628
expect do
2729
described_class.report(exception)
@@ -34,6 +36,7 @@
3436
it do
3537
exception = StandardError.new
3638
allow(Datadog.send(:components)).to receive(:telemetry).and_return(nil)
39+
expect(Datadog.logger).to receive(:warn).with(/Fail to send telemetry log/)
3740

3841
expect do
3942
described_class.report(exception, level: :error, description: 'Oops...')
@@ -48,6 +51,7 @@
4851
telemetry = instance_double(Datadog::Core::Telemetry::Component)
4952
allow(Datadog.send(:components)).to receive(:telemetry).and_return(telemetry)
5053
expect(telemetry).to receive(:error).with('description')
54+
expect(Datadog.logger).not_to receive(:warn)
5155

5256
expect { described_class.error('description') }.not_to raise_error
5357
end
@@ -56,6 +60,7 @@
5660
context 'when there is no telemetry component configured' do
5761
it do
5862
allow(Datadog.send(:components)).to receive(:telemetry).and_return(nil)
63+
expect(Datadog.logger).to receive(:warn).with(/Fail to send telemetry log/)
5964

6065
expect { described_class.error('description') }.not_to raise_error
6166
end

0 commit comments

Comments
 (0)