Skip to content

Carry the intercepted HTTP status as an int, rendering it on demand - #12395

Open
dougqh wants to merge 1 commit into
masterfrom
dougqh/http-status-int
Open

Carry the intercepted HTTP status as an int, rendering it on demand#12395
dougqh wants to merge 1 commit into
masterfrom
dougqh/http-status-int

Conversation

@dougqh

@dougqh dougqh commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

What Does This Do

Metadata carried the intercepted HTTP status as a UTF8BytesString, rendered unconditionally when the Metadata was constructed. This carries the int instead and offers both accessors:

  • int getHttpStatusCode() — the status, UNSET_STATUS (0) when the span carries none, matching the convention DDSpanContext already uses for the field.
  • UTF8BytesString getHttpStatusCodeString() — rendered through RadixTreeCache.HTTP_STATUSES for the string-typed protocols.

No wire change. Every existing consumer — TraceMapperV0_4 / V0_5 / V1, CiTestCycleMapperV1, FileBasedPayloadDispatcher, both OTLP encoders — asks for the string in exactly the place it did before, so the bytes on the wire are identical. The presence checks those mappers already perform become int comparisons.

Motivation

Every span with a status paid a cache probe and carried a string, whether or not its serializer wanted one — and a serializer that wants the number had nothing to ask for.

That is the wrong default now that OTLP is a consumer: semantic conventions type http.response.status_code as an integer, so the rendered string is precisely what it cannot use. Emitting the status under its OpenTelemetry name is blocked on this, because the right key with a string value is worse for a semconv consumer than the un-renamed Datadog name.

Rendering now happens at the one call site that needs it, so a numeric encoder skips the probe entirely.

Additional Notes

Emitting the status numerically, and under its OpenTelemetry name, is the follow-on this unblocks — see #12354, which deliberately leaves the status un-renamed for exactly this reason.

TraceMapperV1 keeps a String local on purpose: its writeAttribute(Writable, String, Object) debug-logs any non-String value.

New MetadataTest covers the accessor pair: the int round-trips, an absent status renders as null rather than "0", and two spans with the same status share one UTF8BytesString — the property that makes routing through the cache worth doing at all.

Contributor Checklist

  • Format code with ./gradlew spotlessApply
  • :dd-trace-core:test green apart from TracerConnectionReliabilityTest, which fails identically on a clean master worktree here (it wants a local agent), and PendingTraceBufferTest, which passes in isolation
  • Title follows the contribution guidelines
  • type:, comp:, tag: ai generated and tag: no release notes labels assigned
  • No new configuration flags or public behaviors to document
  • No source file addition, migration, or deletion — CODEOWNERS unchanged

Jira ticket

N/A

🤖 Generated with Claude Code

Metadata held the intercepted HTTP status as a UTF8BytesString, rendered
unconditionally at construction. Every span with a status therefore paid a
RadixTreeCache probe and carried a string, whether or not its serializer
wanted one -- and a serializer that wants the number had nothing to ask for.
That is the wrong default now that OTLP is one of the consumers: semantic
conventions type http.response.status_code as an integer, so the string is
precisely what it cannot use.

Carry the int instead, and offer both accessors: getHttpStatusCode() returns
the status (UNSET_STATUS when the span carries none, matching DDSpanContext's
own convention), getHttpStatusCodeString() renders it through
RadixTreeCache.HTTP_STATUSES for the string-typed protocols. Rendering moves
from construction to the one call site that needs it, so the presence checks
the mappers already perform become int comparisons and a numeric encoder skips
the probe entirely.

No wire change: every existing consumer asks for the string exactly where it
did before. Emitting the status numerically -- and under its OpenTelemetry
name -- is the follow-on this unblocks.
@dougqh dougqh added comp: core Tracer core tag: ai generated Largely based on code generated by an AI or LLM tag: no release notes Changes to exclude from release notes type: refactoring labels Sep 3, 2026
@datadog-datadog-prod-us1

This comment has been minimized.

@dd-octo-sts

dd-octo-sts Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
Startup 🟢 pass

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 14.80 s 14.71 s [-0.2%; +1.5%] (no difference)
startup:insecure-bank:tracing:Agent 13.64 s 13.74 s [-1.6%; +0.2%] (no difference)
startup:petclinic:appsec:Agent 17.10 s 17.05 s [-0.6%; +1.2%] (no difference)
startup:petclinic:iast:Agent 16.99 s 16.93 s [-0.5%; +1.2%] (no difference)
startup:petclinic:profiling:Agent 16.60 s 16.99 s [-3.3%; -1.3%] (significantly better)
startup:petclinic:sca:Agent 16.94 s 16.86 s [-0.5%; +1.5%] (no difference)
startup:petclinic:tracing:Agent 16.19 s 16.23 s [-1.3%; +0.8%] (no difference)

Commit: 04bb58ab · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

measured,
topLevel,
httpStatusCode == 0 ? null : HTTP_STATUSES.get(httpStatusCode),
httpStatusCode,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This should be a small performance improvement for the application threads, since we're avoiding an expensive cache lookup.

However given that this is more of a functional change and the necessary benchmark doesn't yet exist, I'm going to leave the benchmarking to another PR.

@dougqh
dougqh marked this pull request as ready for review September 3, 2026 12:36
@dougqh
dougqh requested review from a team as code owners September 3, 2026 12:36
@dougqh
dougqh requested review from bric3 and mcculls and removed request for a team September 3, 2026 12:36

@datadog-datadog-prod-us1 datadog-datadog-prod-us1 Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Datadog Autotest: PASS

More details

The code passes the integer HTTP status from DDSpanContext to Metadata. Each changed writer checks UNSET_STATUS before it gets the cached string value, so the wire value does not change.

Was this helpful? React 👍 or 👎

Open Bits AI session

🤖 Datadog Autotest · Commit 04bb58a · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest

@mcculls mcculls left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: core Tracer core tag: ai generated Largely based on code generated by an AI or LLM tag: no release notes Changes to exclude from release notes type: refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants