From 25dcc81f3153b83daea626e9bd547abac9c3d2da Mon Sep 17 00:00:00 2001 From: Milan Garnier Date: Thu, 27 Aug 2026 10:19:59 +0200 Subject: [PATCH 1/2] refactor(propagation): parse otel tracestate members --- .../propagation/ptags/OtelTraceState.java | 30 ++++ .../core/propagation/ptags/PTagsFactory.java | 15 ++ .../core/propagation/ptags/W3CPTagsCodec.java | 152 +++++++++++++----- 3 files changed, 160 insertions(+), 37 deletions(-) create mode 100644 dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/OtelTraceState.java diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/OtelTraceState.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/OtelTraceState.java new file mode 100644 index 00000000000..3b8e837b5b9 --- /dev/null +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/OtelTraceState.java @@ -0,0 +1,30 @@ +package datadog.trace.core.propagation.ptags; + +final class OtelTraceState { + private final String value; + private final int inheritedPosition; + + private OtelTraceState(String value, int inheritedPosition) { + this.value = value; + this.inheritedPosition = inheritedPosition; + } + + static OtelTraceState parse(String raw, int inheritedPosition) { + if (raw == null || raw.isEmpty()) { + return null; + } + return new OtelTraceState(raw, inheritedPosition); + } + + String getValue() { + return value; + } + + int length() { + return value.length(); + } + + int getInheritedPosition() { + return inheritedPosition; + } +} diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsFactory.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsFactory.java index 0b5184d448a..1661398c819 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsFactory.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/PTagsFactory.java @@ -112,6 +112,8 @@ static class PTags extends PropagationTags { private volatile TagValue orgPropagationMarkerTagValue; + private volatile OtelTraceState otelTraceState; + // Static cache for the most-recently-seen rate → TagValue. In steady state a service uses one // rate, so this eliminates the char[] + String allocation on every new PTags instance. // Writes are benign-racy: two threads computing the same rate produce equal TagValues. @@ -540,7 +542,20 @@ public String getW3CTracestate() { @Override public void updateW3CTracestate(String tracestate) { + clearCachedHeader(W3C); this.tracestate = tracestate; + setOtelTraceState(W3CPTagsCodec.extractOtelTraceState(tracestate)); + } + + OtelTraceState getOtelTraceState() { + return otelTraceState; + } + + void setOtelTraceState(OtelTraceState otelTraceState) { + if (this.otelTraceState != otelTraceState) { + this.otelTraceState = otelTraceState; + clearCachedHeader(W3C); + } } String getError() { diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java index c0018544188..d56a0076017 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java @@ -21,6 +21,7 @@ public class W3CPTagsCodec extends PTagsCodec { private static final int MAX_HEADER_SIZE = 256; private static final String DATADOG_MEMBER_KEY = "dd="; + private static final String OTEL_MEMBER_KEY = "ot="; private static final int EMPTY_SIZE = DATADOG_MEMBER_KEY.length(); // 3 private static final char MEMBER_SEPARATOR = ','; private static final char ELEMENT_SEPARATOR = ';'; @@ -86,7 +87,7 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { if (ddMemberIndex == -1) { // There was no dd member, so create an empty one with the _suffix_ - return empty(tagsFactory, value); + return empty(tagsFactory, value, extractOtelTraceState(value)); } List tagPairs = null; @@ -201,7 +202,8 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { ddMemberValueEnd, maxUnknownSize, lastParentId, - orgPropagationMarkerTagValue); + orgPropagationMarkerTagValue, + extractOtelTraceState(value)); } @Override @@ -226,6 +228,10 @@ protected int estimateHeaderSize(PTags pTags) { // We assume there is no Datadog list-member size += pTags.tracestate.length(); } + OtelTraceState otelTraceState = pTags.getOtelTraceState(); + if (otelTraceState != null) { + size += OTEL_MEMBER_KEY.length() + otelTraceState.length() + 1; + } return size; } @@ -290,9 +296,8 @@ protected int appendSuffix(StringBuilder sb, PTags ptags, int size) { sb.setLength(0); size = 0; } - // Append all other non-Datadog list-members - int newSize = cleanUpAndAppendSuffix(sb, ptags, size); - if (newSize != size) { + // Append the managed OTel member and all other non-Datadog list-members + if (appendOtelAndVendorMembers(sb, ptags, size != 0)) { // We don't care about the total size in bytes here, but only the fact that we added something // that should be returned size = Math.max(size, EMPTY_SIZE + 1); @@ -698,50 +703,104 @@ private static int cleanUpAndAppendUnknown(StringBuilder sb, W3CPTags w3CPTags, return size; } - private static int cleanUpAndAppendSuffix(StringBuilder sb, PTags ptags, int size) { + private static boolean appendOtelAndVendorMembers( + StringBuilder sb, PTags ptags, boolean hasDatadogMember) { String original = ptags.tracestate; - if (original == null) { - return size; - } - int ddMemberStart = (ptags instanceof W3CPTags) ? ((W3CPTags) ptags).ddMemberStart : -1; - int remainingMemberAllowed = size == 0 ? MAX_MEMBER_COUNT : MAX_MEMBER_COUNT - 1; - int len = original.length(); - int memberStart = findNextMember(original, 0); - while (memberStart < len) { + OtelTraceState otelTraceState = ptags.getOtelTraceState(); + int remainingMembers = MAX_MEMBER_COUNT - (hasDatadogMember ? 1 : 0); + int otherMemberPosition = 0; + boolean otelTraceStateAppended = false; + boolean memberAppended = false; + int len = original == null ? 0 : original.length(); + int memberStart = original == null ? 0 : findNextMember(original, 0); + while (memberStart < len && remainingMembers > 0) { // Look for member end position int memberEnd = original.indexOf(MEMBER_SEPARATOR, memberStart); if (memberEnd < 0) { memberEnd = len; } - // Try to define Datadog member start if not already found - if (ddMemberStart == -1) { - if (original.startsWith(DATADOG_MEMBER_KEY, memberStart)) { - ddMemberStart = memberStart; - } - } - // Skip Datadog member (already added with prefix and tags) - if (memberStart != ddMemberStart) { - if (sb.length() > 0) { - sb.append(MEMBER_SEPARATOR); - size++; + boolean managedMember = + original.startsWith(DATADOG_MEMBER_KEY, memberStart) + || original.startsWith(OTEL_MEMBER_KEY, memberStart); + if (!managedMember) { + if (otelTraceState != null + && !otelTraceStateAppended + && otelTraceState.getInheritedPosition() == otherMemberPosition) { + appendMember(sb, OTEL_MEMBER_KEY, otelTraceState.getValue()); + remainingMembers--; + otelTraceStateAppended = true; + memberAppended = true; + if (remainingMembers == 0) { + break; + } } int end = stripTrailingOWC(original, memberStart, memberEnd); - sb.append(original, memberStart, end); - size += (end - memberStart); - remainingMemberAllowed--; + appendMember(sb, original, memberStart, end); + remainingMembers--; + otherMemberPosition++; + memberAppended = true; } - // Check if remaining members are allowed - if (remainingMemberAllowed == 0) { - memberStart = len; - } else { - memberStart = findNextMember(original, memberEnd + 1); + memberStart = findNextMember(original, memberEnd + 1); + } + if (otelTraceState != null + && !otelTraceStateAppended + && remainingMembers > 0 + && otelTraceState.getInheritedPosition() == otherMemberPosition) { + appendMember(sb, OTEL_MEMBER_KEY, otelTraceState.getValue()); + memberAppended = true; + } + return memberAppended; + } + + private static void appendMember(StringBuilder sb, String member, int start, int end) { + if (sb.length() != 0) { + sb.append(MEMBER_SEPARATOR); + } + sb.append(member, start, end); + } + + private static void appendMember(StringBuilder sb, String key, String value) { + if (sb.length() != 0) { + sb.append(MEMBER_SEPARATOR); + } + sb.append(key).append(value); + } + + static OtelTraceState extractOtelTraceState(String tracestate) { + if (tracestate == null || tracestate.isEmpty()) { + return null; + } + int otherMemberPosition = 0; + int memberStart = findNextMember(tracestate, 0); + while (memberStart < tracestate.length()) { + int memberValueStart = validateMemberKey(tracestate, memberStart); + if (memberValueStart < 0) { + return null; } + int memberValueEnd = validateMemberValue(tracestate, memberValueStart); + if (memberValueEnd < 0) { + return null; + } + if (tracestate.startsWith(OTEL_MEMBER_KEY, memberStart)) { + int end = stripTrailingOWC(tracestate, memberValueStart, memberValueEnd); + return OtelTraceState.parse( + tracestate.substring(memberValueStart, end), otherMemberPosition); + } + if (!tracestate.startsWith(DATADOG_MEMBER_KEY, memberStart)) { + otherMemberPosition++; + } + memberStart = findNextMember(tracestate, memberValueEnd); } - return size; + return null; } static W3CPTags empty(PTagsFactory factory, String original) { - return empty(factory, original, 0, -1, -1); + return empty(factory, original, extractOtelTraceState(original)); + } + + private static W3CPTags empty( + PTagsFactory factory, String original, OtelTraceState otelTraceState) { + return empty(factory, original, 0, -1, -1, otelTraceState); } private static W3CPTags empty( @@ -750,6 +809,22 @@ private static W3CPTags empty( int firstMemberStart, int ddMemberStart, int ddMemberValueEnd) { + return empty( + factory, + original, + firstMemberStart, + ddMemberStart, + ddMemberValueEnd, + extractOtelTraceState(original)); + } + + private static W3CPTags empty( + PTagsFactory factory, + String original, + int firstMemberStart, + int ddMemberStart, + int ddMemberValueEnd, + OtelTraceState otelTraceState) { return new W3CPTags( factory, null, @@ -764,7 +839,8 @@ private static W3CPTags empty( ddMemberValueEnd, 0, null, - null); + null, + otelTraceState); } private static class W3CPTags extends PTags { @@ -799,7 +875,8 @@ public W3CPTags( int ddMemberValueEnd, int maxUnknownSize, CharSequence lastParentId, - TagValue orgPropagationMarkerTagValue) { + TagValue orgPropagationMarkerTagValue, + OtelTraceState otelTraceState) { super( factory, tagPairs, @@ -815,6 +892,7 @@ public W3CPTags( this.ddMemberStart = ddMemberStart; this.ddMemberValueEnd = ddMemberValueEnd; this.maxUnknownSize = maxUnknownSize; + setOtelTraceState(otelTraceState); } @Override From 397a1ff69a711b8d43542f7d87331e7f664bc2c5 Mon Sep 17 00:00:00 2001 From: MilanGarnier Date: Fri, 4 Sep 2026 17:26:43 +0200 Subject: [PATCH 2/2] perf(propagation): avoid redundant tracestate parsing --- .../core/propagation/ptags/W3CPTagsCodec.java | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java index d56a0076017..1ff92c4b6e0 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java @@ -50,13 +50,19 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { int ddMemberValueEnd = -1; // dd member value end position including OWS (exclusive) int memberIndex = 0; int ddMemberIndex = -1; + int otelMemberValueStart = -1; + int otelMemberValueEnd = -1; + int otelMemberPosition = 0; + int otherMemberPosition = 0; while (memberStart < len) { if (memberIndex == MAX_MEMBER_COUNT) { // TODO should we return one with an error? // TODO should we try to pick up the `dd` member anyway? return tagsFactory.empty(); } - if (ddMemberIndex == -1 && value.startsWith(DATADOG_MEMBER_KEY, memberStart)) { + boolean datadogMember = value.startsWith(DATADOG_MEMBER_KEY, memberStart); + boolean otelMember = value.startsWith(OTEL_MEMBER_KEY, memberStart); + if (ddMemberIndex == -1 && datadogMember) { ddMemberStart = memberStart; ddMemberIndex = memberIndex; } @@ -69,6 +75,7 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { if (ddMemberValueStart == -1 && ddMemberIndex != -1) { ddMemberValueStart = pos; } + int memberValueStart = pos; pos = validateMemberValue(value, pos); if (pos < 0) { // TODO should we return one with an error? @@ -77,6 +84,15 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { if (ddMemberValueEnd == -1 && ddMemberIndex != -1) { ddMemberValueEnd = pos; } + if (otelMemberValueStart == -1) { + if (otelMember) { + otelMemberValueStart = memberValueStart; + otelMemberValueEnd = stripTrailingOWC(value, memberValueStart, pos); + otelMemberPosition = otherMemberPosition; + } else if (!datadogMember) { + otherMemberPosition++; + } + } memberStart = findNextMember(value, pos); if (memberStart < 0) { // TODO should we return one with an error? @@ -85,9 +101,15 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { memberIndex++; } + OtelTraceState otelTraceState = + otelMemberValueStart < 0 + ? null + : OtelTraceState.parse( + value.substring(otelMemberValueStart, otelMemberValueEnd), otelMemberPosition); + if (ddMemberIndex == -1) { // There was no dd member, so create an empty one with the _suffix_ - return empty(tagsFactory, value, extractOtelTraceState(value)); + return empty(tagsFactory, value, otelTraceState); } List tagPairs = null; @@ -159,7 +181,13 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { if (tagKey.equals(TRACE_ID_TAG)) { return tagsFactory.createInvalid(PROPAGATION_ERROR_MALFORMED_TID + tagValue); } - return empty(tagsFactory, value, firstMemberStart, ddMemberStart, ddMemberValueEnd); + return empty( + tagsFactory, + value, + firstMemberStart, + ddMemberStart, + ddMemberValueEnd, + otelTraceState); } if (tagKey.equals(DECISION_MAKER_TAG)) { decisionMakerTagValue = tagValue; @@ -203,7 +231,7 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { maxUnknownSize, lastParentId, orgPropagationMarkerTagValue, - extractOtelTraceState(value)); + otelTraceState); } @Override @@ -803,21 +831,6 @@ private static W3CPTags empty( return empty(factory, original, 0, -1, -1, otelTraceState); } - private static W3CPTags empty( - PTagsFactory factory, - String original, - int firstMemberStart, - int ddMemberStart, - int ddMemberValueEnd) { - return empty( - factory, - original, - firstMemberStart, - ddMemberStart, - ddMemberValueEnd, - extractOtelTraceState(original)); - } - private static W3CPTags empty( PTagsFactory factory, String original,