Skip to content

Commit

Permalink
Changed: Lazily clone trace context after a fork.
Browse files Browse the repository at this point in the history
  • Loading branch information
delner committed Oct 29, 2020
1 parent 619dd5f commit daae013
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 4 deletions.
13 changes: 12 additions & 1 deletion lib/ddtrace/context_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,18 @@ def context=(ctx)

# Return the local context.
def context(key = nil)
key.nil? ? @context.local : @context.local(key)
current_context = key.nil? ? @context.local : @context.local(key)

# Rebuild/reset context after a fork
#
# We don't want forked processes to copy and retransmit spans
# that were generated from the parent process. Reset it such
# that it acts like a distributed trace.
current_context.after_fork! do
current_context = self.context = current_context.fork_clone
end

current_context
end
end

Expand Down
33 changes: 30 additions & 3 deletions spec/ddtrace/context_provider_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
RSpec.describe Datadog::DefaultContextProvider do
let(:provider) { described_class.new }
let(:local_context) { instance_double(Datadog::ThreadLocalContext) }
let(:trace_context) { Datadog::Context.new }

context '#context=' do
subject(:context=) { provider.context = ctx }
Expand All @@ -25,7 +26,10 @@

context 'when given no arguments' do
it do
expect(local_context).to receive(:local)
expect(local_context)
.to receive(:local)
.and_return(trace_context)

subject
end
end
Expand All @@ -38,22 +42,45 @@
expect(local_context)
.to receive(:local)
.with(key)
.and_return(trace_context)

subject
end
end
end

context 'when fork occurs' do
before { skip 'Java not supported' if RUBY_PLATFORM == 'java' }

it 'clones the context and returns the clone' do
# Initialize a context for the current process
parent_context = provider.context
expect(parent_context.forked?).to be false

# Fork the process, clone context.
expect_in_fork do
expect(parent_context).to receive(:fork_clone).and_call_original
child_context = provider.context

# Check context changed
expect(child_context).to_not be parent_context

# Check context doesn't change again
expect(provider.context).to be(child_context)
end
end
end

context 'with multiple instances' do
it 'holds independent values for each instance' do
provider1 = described_class.new
provider2 = described_class.new

ctx1 = provider1.context = double
ctx1 = provider1.context = Datadog::Context.new
expect(provider1.context).to be(ctx1)
expect(provider2.context).to_not be(ctx1)

ctx2 = provider2.context = double
ctx2 = provider2.context = Datadog::Context.new
expect(provider1.context).to be(ctx1)
expect(provider2.context).to be(ctx2)
end
Expand Down

0 comments on commit daae013

Please sign in to comment.