Skip to content

Commit dbfdb19

Browse files
committed
Add support for checkpointActiveForRollback...rollbackActiveToCheckpoint to clean up leaked scopes
1 parent adf6612 commit dbfdb19

File tree

4 files changed

+94
-9
lines changed

4 files changed

+94
-9
lines changed

dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,16 @@ public AgentScope activeScope() {
977977
return scopeManager.active();
978978
}
979979

980+
@Override
981+
public void checkpointActiveForRollback() {
982+
this.scopeManager.checkpointActiveForRollback();
983+
}
984+
985+
@Override
986+
public void rollbackActiveToCheckpoint() {
987+
this.scopeManager.rollbackActiveToCheckpoint();
988+
}
989+
980990
@Override
981991
public void closeActive() {
982992
AgentScope activeScope = this.scopeManager.active();

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

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@ class ContinuableScope implements AgentScope, AttachableWrapper {
1515

1616
final AgentSpan span; // package-private so scopeManager can access it directly
1717

18-
/** Flag to propagate this scope across async boundaries. */
19-
private boolean isAsyncPropagating;
18+
/** Flag that this scope should be allowed to propagate across async boundaries. */
19+
private static final byte ASYNC_PROPAGATING = 1;
2020

21-
private final byte flags;
21+
/** Flag that we intend to roll back the scope stack to this scope in the future. */
22+
private static final byte CHECKPOINTED = 2;
23+
24+
private byte flags;
25+
26+
private final byte source;
2227

2328
private short referenceCount = 1;
2429

@@ -36,8 +41,8 @@ class ContinuableScope implements AgentScope, AttachableWrapper {
3641
final Stateful scopeState) {
3742
this.scopeManager = scopeManager;
3843
this.span = span;
39-
this.flags = source;
40-
this.isAsyncPropagating = isAsyncPropagating;
44+
this.source = source;
45+
this.flags = isAsyncPropagating ? ASYNC_PROPAGATING : 0;
4146
this.scopeState = scopeState;
4247
}
4348

@@ -116,7 +121,7 @@ final boolean alive() {
116121

117122
@Override
118123
public final boolean isAsyncPropagating() {
119-
return isAsyncPropagating;
124+
return (flags & ASYNC_PROPAGATING) != 0;
120125
}
121126

122127
@Override
@@ -126,14 +131,31 @@ public final AgentSpan span() {
126131

127132
@Override
128133
public final void setAsyncPropagation(final boolean value) {
129-
isAsyncPropagating = value;
134+
if (value) {
135+
flags |= ASYNC_PROPAGATING;
136+
} else {
137+
flags &= ~ASYNC_PROPAGATING;
138+
}
130139
}
131140

132141
@Override
133142
public final String toString() {
134143
return super.toString() + "->" + span;
135144
}
136145

146+
public void checkpoint() {
147+
flags |= CHECKPOINTED;
148+
}
149+
150+
public boolean rollback() {
151+
if ((flags & CHECKPOINTED) != 0) {
152+
flags &= ~CHECKPOINTED;
153+
return false;
154+
} else {
155+
return true;
156+
}
157+
}
158+
137159
public final void beforeActivated() {
138160
try {
139161
scopeState.activate(span.context());
@@ -164,7 +186,7 @@ public final void afterActivated() {
164186

165187
@Override
166188
public byte source() {
167-
return (byte) (flags & 0x7F);
189+
return (byte) (source & 0x7F);
168190
}
169191

170192
@Override

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,24 @@ public AgentScope active() {
218218
return scopeStack().active();
219219
}
220220

221+
public void checkpointActiveForRollback() {
222+
ContinuableScope active = scopeStack().active();
223+
if (active != null) {
224+
active.checkpoint();
225+
}
226+
}
227+
228+
public void rollbackActiveToCheckpoint() {
229+
ContinuableScope active;
230+
while ((active = scopeStack().active()) != null) {
231+
if (active.rollback()) {
232+
active.close();
233+
} else {
234+
break; // stop at the most recent checkpointed scope
235+
}
236+
}
237+
}
238+
221239
public AgentSpan activeSpan() {
222240
final ContinuableScope active = scopeStack().active();
223241
return active == null ? null : active.span;

internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/AgentTracer.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,35 @@ public static AgentScope.Continuation captureSpan(final AgentSpan span) {
121121
return get().captureSpan(span);
122122
}
123123

124+
/**
125+
* Checkpoints the active scope. A subsequent call to {@link #rollbackActiveToCheckpoint()} closes
126+
* outstanding scopes up to but not including the most recent checkpointed scope.
127+
*
128+
* @deprecated This should only be used when scopes might leak onto the scope stack which cannot
129+
* be cleaned up by other means.
130+
*/
131+
@Deprecated
132+
public static void checkpointActiveForRollback() {
133+
get().checkpointActiveForRollback();
134+
}
135+
136+
/**
137+
* Closes outstanding scopes up to but not including the most recent scope checkpointed with
138+
* {@link #checkpointActiveForRollback()}. Closes all scopes if none have been checkpointed.
139+
*
140+
* @deprecated This should only be used when scopes have leaked onto the scope stack that cannot
141+
* be cleaned up by other means.
142+
*/
143+
@Deprecated
144+
public static void rollbackActiveToCheckpoint() {
145+
get().rollbackActiveToCheckpoint();
146+
}
147+
124148
/**
125149
* Closes the scope for the currently active span.
126150
*
127-
* @deprecated Prefer closing the scope returned by {@link #activateSpan} when available.
151+
* @deprecated This should only be used when an instrumentation does not have access to the
152+
* original scope returned by {@link #activateSpan}.
128153
*/
129154
@Deprecated
130155
public static void closeActive() {
@@ -322,6 +347,10 @@ AgentSpan startSpan(
322347

323348
AgentScope.Continuation captureSpan(AgentSpan span);
324349

350+
void checkpointActiveForRollback();
351+
352+
void rollbackActiveToCheckpoint();
353+
325354
void closeActive();
326355

327356
void closePrevious(boolean finishSpan);
@@ -477,6 +506,12 @@ public boolean isAsyncPropagationEnabled() {
477506
@Override
478507
public void setAsyncPropagationEnabled(boolean asyncPropagationEnabled) {}
479508

509+
@Override
510+
public void checkpointActiveForRollback() {}
511+
512+
@Override
513+
public void rollbackActiveToCheckpoint() {}
514+
480515
@Override
481516
public void closeActive() {}
482517

0 commit comments

Comments
 (0)