Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
d2cc4ec
Experimenting with introducing CoreSpanBuilder reuse
dougqh Sep 12, 2025
d83ff16
Adding protection against reusing a CoreSpanBuilder that's still in-use
dougqh Sep 15, 2025
045e4c9
spotless
dougqh Sep 15, 2025
95e6f17
Adding configuration option to control reuse of SpanBuilders
dougqh Sep 15, 2025
55b56e7
Adding explanatory comments
dougqh Sep 15, 2025
8de4b30
Enabling by default
dougqh Sep 16, 2025
8adfe8f
Merge branch 'master' into dougqh/spanbuilder-pooling
dougqh Sep 16, 2025
5763a31
Adding reuse tests
dougqh Sep 16, 2025
353afa2
Merge branch 'dougqh/spanbuilder-pooling' of github.com:DataDog/dd-tr…
dougqh Sep 16, 2025
23b03b8
spotless
dougqh Sep 16, 2025
b588b7a
Merge branch 'master' into dougqh/spanbuilder-pooling
dougqh Sep 16, 2025
68a6f03
Changed the API to be safe for atypical usage
dougqh Sep 16, 2025
ec730ec
Merge branch 'dougqh/spanbuilder-pooling' of github.com:DataDog/dd-tr…
dougqh Sep 16, 2025
2d22b41
Fleshing out tests
dougqh Sep 17, 2025
55bd500
spotless
dougqh Sep 17, 2025
2258db5
Merge branch 'master' into dougqh/spanbuilder-pooling
dougqh Sep 17, 2025
079d1f5
Improving single threaded performance
dougqh Sep 17, 2025
a55095e
spotless
dougqh Sep 17, 2025
26482bf
Merge branch 'dougqh/spanbuilder-pooling' of github.com:DataDog/dd-tr…
dougqh Sep 17, 2025
e5161ef
Fixing test that renaming didn't update properly
dougqh Sep 18, 2025
40ba13d
Adding benchmarks for span creation
dougqh Sep 18, 2025
70eccff
spotless
dougqh Sep 18, 2025
5b594a2
Adding clarifying comments & more tests
dougqh Sep 18, 2025
65fa1d7
spotless
dougqh Sep 18, 2025
136329e
tweaking comments
dougqh Sep 18, 2025
2e7da54
More comments
dougqh Sep 18, 2025
98d5603
More comment clean-up
dougqh Sep 18, 2025
ff9acbe
Addressing review feedback
dougqh Sep 19, 2025
4475e42
Adding overload to just check major version
dougqh Oct 7, 2025
4d2ec09
Adding ThreadUtils
dougqh Oct 7, 2025
aea9d3b
Adding isVirtualThread check to reuseSingleSpanBuilder
dougqh Oct 7, 2025
8bb8f29
Merge branch 'master' into dougqh/spanbuilder-pooling
dougqh Oct 7, 2025
6ed469b
Addressing review comments - reduced visibility
dougqh Oct 7, 2025
e26959d
Merge branch 'master' into dougqh/spanbuilder-pooling
dougqh Oct 7, 2025
66f62fb
Update dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
dougqh Oct 14, 2025
94970a4
Update internal-api/src/main/java/datadog/trace/bootstrap/instrumenta…
dougqh Oct 14, 2025
388a288
Merge branch 'master' into dougqh/spanbuilder-pooling
dougqh Oct 14, 2025
86c03d9
Update dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
dougqh Oct 14, 2025
8e3ee47
Fixing bad update from GitHub suggestion merge
dougqh Oct 15, 2025
0b968ae
Adding Javadoc
dougqh Oct 15, 2025
097e742
spotless
dougqh Oct 15, 2025
826f528
Update CoreTracer.java
dougqh Oct 15, 2025
bd4b957
Updating comments to reflect the usage logic was moved into start
dougqh Oct 15, 2025
e9cf952
Tweaking comments
dougqh Oct 15, 2025
55169c7
Merge branch 'master' into dougqh/spanbuilder-pooling
dougqh Oct 15, 2025
eb2996d
Improving test coverage
dougqh Oct 16, 2025
5fd6e1d
spotless
dougqh Oct 16, 2025
e46f8f5
A bit of clean-up - introducing some helper methods
dougqh Oct 16, 2025
054485a
Fixing v-thread tests
dougqh Oct 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ public final class GeneralConfig {
public static final String OPTIMIZED_MAP_ENABLED = "optimized.map.enabled";
public static final String TAG_NAME_UTF8_CACHE_SIZE = "tag.name.utf8.cache.size";
public static final String TAG_VALUE_UTF8_CACHE_SIZE = "tag.value.utf8.cache.size";
public static final String SPAN_BUILDER_REUSE_ENABLED = "span.builder.reuse.enabled";
Copy link
Contributor Author

@dougqh dougqh Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is best class to be placing these in. Or if this is the proper namespace. dd.trace... might be better

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree dd.trace prefix fits better

public static final String STACK_TRACE_LENGTH_LIMIT = "stack.trace.length.limit";

public static final String SSI_INJECTION_ENABLED = "injection.enabled";
Expand Down
97 changes: 88 additions & 9 deletions dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,22 @@ public static CoreTracerBuilder builder() {

private final PropagationTags.Factory propagationTagsFactory;

// DQH - storing into a static constant, so value will constant propagate and dead code eliminate
// the other branch in buildSpan
private static final boolean SPAN_BUILDER_REUSE_ENABLED =
Config.get().isSpanBuilderReuseEnabled();

// Cache used by buildSpan - instance so it can capture the CoreTracer
private final ThreadLocal<CoreSpanBuilder> tlSpanBuilder =
SPAN_BUILDER_REUSE_ENABLED
? new ThreadLocal<CoreSpanBuilder>() {
@Override
protected CoreSpanBuilder initialValue() {
return new CoreSpanBuilder(CoreTracer.this);
}
}
: null;

@Override
public ConfigSnapshot captureTraceConfig() {
return dynamicConfig.captureTraceConfig();
Expand Down Expand Up @@ -968,7 +984,42 @@ long getTimeWithNanoTicks(long nanoTicks) {
@Override
public CoreSpanBuilder buildSpan(
final String instrumentationName, final CharSequence operationName) {
return new CoreSpanBuilder(this, instrumentationName, operationName);
return SPAN_BUILDER_REUSE_ENABLED
? this.reuseSpanBuilder(instrumentationName, operationName)
: this.createSpanBuilder(instrumentationName, operationName);
}

private CoreSpanBuilder createSpanBuilder(
final String instrumentationName, final CharSequence operationName) {
CoreSpanBuilder newBuilder = new CoreSpanBuilder(this);
newBuilder.reset(instrumentationName, operationName);
return newBuilder;
}

private CoreSpanBuilder reuseSpanBuilder(
final String instrumentationName, final CharSequence operationName) {
// retrieve the thread's typical SpanBuilder and check if it is currently in use
CoreSpanBuilder tlSpanBuilder = this.tlSpanBuilder.get();
boolean wasReset = tlSpanBuilder.reset(instrumentationName, operationName);
if (wasReset) return tlSpanBuilder;

// TODO: counter for how often the fallback is used?
CoreSpanBuilder newSpanBuilder = new CoreSpanBuilder(this);
newSpanBuilder.reset(instrumentationName, operationName);

// DQH - Debated how best to handle the case of someone requesting a CoreSpanBuilder
// and then not using it. Without an ability to replace the cached CoreSpanBuilder,
// that case could result in permanently burning the cache for a given thread.

// That could be solved with additional logic during CoreSpanBuilder#build
// that checks to see if the cached CoreSpanBuilder is in use and then replaces it
// with the freed CoreSpanBuilder, but that would put extra logic in the common path.

// Instead of making the release process more complicating, I'm chosing to just
// override here when we know we're already in an uncommon path.
this.tlSpanBuilder.set(newSpanBuilder);

return newSpanBuilder;
}

@Override
Expand Down Expand Up @@ -1400,11 +1451,18 @@ private static <K, V> Map<V, K> invertMap(Map<K, V> map) {
}

/** Spans are built using this builder */
public static class CoreSpanBuilder implements AgentTracer.SpanBuilder {
private final String instrumentationName;
private final CharSequence operationName;
/*
* To reduce overhead, CoreSpanBuilders are reused.
* For simplicity, CoreSpanBuilder isn't thread safe, so CoreSpanBuilders can only be reused within one thread.
*/
public static final class CoreSpanBuilder implements AgentTracer.SpanBuilder {
private final CoreTracer tracer;

private boolean inUse = false;

private String instrumentationName;
private CharSequence operationName;

// Builder attributes
private TagMap.Ledger tagLedger;
private long timestampMicro;
Expand All @@ -1420,13 +1478,32 @@ public static class CoreSpanBuilder implements AgentTracer.SpanBuilder {
private List<AgentSpanLink> links;
private long spanId;

CoreSpanBuilder(
final CoreTracer tracer,
final String instrumentationName,
final CharSequence operationName) {
CoreSpanBuilder(CoreTracer tracer) {
this.tracer = tracer;
}

boolean reset(String instrumentationName, CharSequence operationName) {
if (this.inUse) return false;
this.inUse = true;

this.instrumentationName = instrumentationName;
this.operationName = operationName;
this.tracer = tracer;

if (this.tagLedger != null) this.tagLedger.reset();
this.timestampMicro = 0L;
this.parent = null;
this.serviceName = null;
this.resourceName = null;
this.errorFlag = false;
this.spanType = null;
this.ignoreScope = false;
this.builderRequestContextDataAppSec = null;
this.builderRequestContextDataIast = null;
this.builderCiVisibilityContextData = null;
this.links = null;
this.spanId = 0L;

return true;
}

@Override
Expand All @@ -1443,6 +1520,8 @@ private DDSpan buildSpan() {
span.setEndpointTracker(tracker);
}
}

this.inUse = false;
return span;
}

Expand Down
7 changes: 7 additions & 0 deletions internal-api/src/main/java/datadog/trace/api/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,7 @@ public static String getHostName() {
private final boolean jdkSocketEnabled;

private final boolean optimizedMapEnabled;
private final boolean spanBuilderReuseEnabled;
private final int tagNameUtf8CacheSize;
private final int tagValueUtf8CacheSize;
private final int stackTraceLengthLimit;
Expand Down Expand Up @@ -2749,6 +2750,8 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment())

this.optimizedMapEnabled =
configProvider.getBoolean(GeneralConfig.OPTIMIZED_MAP_ENABLED, false);
this.spanBuilderReuseEnabled =
configProvider.getBoolean(GeneralConfig.SPAN_BUILDER_REUSE_ENABLED, false);
this.tagNameUtf8CacheSize =
Math.max(configProvider.getInteger(GeneralConfig.TAG_NAME_UTF8_CACHE_SIZE, 128), 0);
this.tagValueUtf8CacheSize =
Expand Down Expand Up @@ -4474,6 +4477,10 @@ public boolean isOptimizedMapEnabled() {
return optimizedMapEnabled;
}

public boolean isSpanBuilderReuseEnabled() {
return spanBuilderReuseEnabled;
}

public int getTagNameUtf8CacheSize() {
return tagNameUtf8CacheSize;
}
Expand Down
Loading