Skip to content

Commit b56bb15

Browse files
authored
When activating a span merge it with any non-span context (#8649)
1 parent f020e29 commit b56bb15

File tree

2 files changed

+78
-4
lines changed

2 files changed

+78
-4
lines changed

dd-trace-core/src/main/java/datadog/trace/core/scopemanager/ContinuableScopeManager.java

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,14 @@ private AgentScope.Continuation captureSpan(final AgentSpan span, byte source) {
119119
}
120120

121121
private AgentScope activate(
122-
final Context context,
122+
final AgentSpan span,
123123
final byte source,
124124
final boolean overrideAsyncPropagation,
125125
final boolean isAsyncPropagating) {
126126
ScopeStack scopeStack = scopeStack();
127127

128128
final ContinuableScope top = scopeStack.top;
129-
if (top != null && top.context.equals(context)) {
129+
if (top != null && span.equals(top.span())) {
130130
top.incrementReferences();
131131
return top;
132132
}
@@ -139,14 +139,16 @@ private AgentScope activate(
139139
return noopScope();
140140
}
141141

142-
assert context != null;
142+
assert span != null;
143143

144144
// Inherit the async propagation from the active scope unless the value is overridden
145145
boolean asyncPropagation =
146146
overrideAsyncPropagation
147147
? isAsyncPropagating
148148
: top != null ? top.isAsyncPropagating() : DEFAULT_ASYNC_PROPAGATING;
149149

150+
Context context = top != null ? top.context.with(span) : span;
151+
150152
final ContinuableScope scope =
151153
new ContinuableScope(this, context, source, asyncPropagation, createScopeState(context));
152154
scopeStack.push(scope);
@@ -155,6 +157,36 @@ private AgentScope activate(
155157
return scope;
156158
}
157159

160+
private AgentScope activate(final Context context) {
161+
ScopeStack scopeStack = scopeStack();
162+
163+
final ContinuableScope top = scopeStack.top;
164+
if (top != null && top.context.equals(context)) {
165+
top.incrementReferences();
166+
return top;
167+
}
168+
169+
// DQH - This check could go before the check above, since depth limit checking is fast
170+
final int currentDepth = scopeStack.depth();
171+
if (depthLimit <= currentDepth) {
172+
healthMetrics.onScopeStackOverflow();
173+
log.debug("Scope depth limit exceeded ({}). Returning NoopScope.", currentDepth);
174+
return noopScope();
175+
}
176+
177+
assert context != null;
178+
179+
// Inherit the async propagation from the active scope
180+
boolean asyncPropagation = top != null ? top.isAsyncPropagating() : DEFAULT_ASYNC_PROPAGATING;
181+
182+
final ContinuableScope scope =
183+
new ContinuableScope(this, context, CONTEXT, asyncPropagation, createScopeState(context));
184+
scopeStack.push(scope);
185+
healthMetrics.onActivateScope();
186+
187+
return scope;
188+
}
189+
158190
/**
159191
* Activates a scope for the given {@link ScopeContinuation}.
160192
*
@@ -326,7 +358,7 @@ public Context current() {
326358

327359
@Override
328360
public ContextScope attach(Context context) {
329-
return activate(context, CONTEXT, false, true);
361+
return activate(context);
330362
}
331363

332364
@Override

dd-trace-core/src/test/groovy/datadog/trace/core/scopemanager/ScopeManagerTest.groovy

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package datadog.trace.core.scopemanager
22

3+
import datadog.context.Context
4+
import datadog.context.ContextKey
35
import datadog.trace.agent.test.utils.ThreadUtils
46
import datadog.trace.api.DDTraceId
57
import datadog.trace.api.Stateful
@@ -1033,6 +1035,46 @@ class ScopeManagerTest extends DDCoreSpecification {
10331035
executor.shutdown()
10341036
}
10351037

1038+
def "activating a span merges it with existing context"() {
1039+
when:
1040+
def span = tracer.buildSpan("test", "test").start()
1041+
def testKey = ContextKey.named("test")
1042+
def context = Context.root().with(testKey, "test-value")
1043+
def contextScope = scopeManager.attach(context)
1044+
1045+
then:
1046+
scopeManager.active() == contextScope
1047+
scopeManager.current() == context
1048+
scopeManager.activeSpan() == null
1049+
scopeManager.current().get(testKey) == "test-value"
1050+
1051+
when:
1052+
def scope = tracer.activateSpan(span)
1053+
1054+
then:
1055+
scopeManager.active() == scope
1056+
scopeManager.current() != context
1057+
scopeManager.activeSpan() == span
1058+
scopeManager.current().get(testKey) == "test-value"
1059+
1060+
when:
1061+
scope.close()
1062+
1063+
then:
1064+
scopeManager.active() == contextScope
1065+
scopeManager.current() == context
1066+
scopeManager.activeSpan() == null
1067+
scopeManager.current().get(testKey) == "test-value"
1068+
1069+
when:
1070+
contextScope.close()
1071+
1072+
then:
1073+
scopeManager.active() == null
1074+
scopeManager.current() == Context.root()
1075+
scopeManager.activeSpan() == null
1076+
}
1077+
10361078
boolean spanFinished(AgentSpan span) {
10371079
return ((DDSpan) span)?.isFinished()
10381080
}

0 commit comments

Comments
 (0)