Skip to content

🍒 12898 - Fix trace and StatsD connections over Windows named pipes - #13241

Open
apiarian-datadog wants to merge 1 commit into
release/v1.68.xfrom
aleksandr.pasechnik/backport-pr-12898
Open

apiarian-datadog wants to merge 1 commit into
release/v1.68.xfrom
aleksandr.pasechnik/backport-pr-12898

Conversation

@apiarian-datadog

Copy link
Copy Markdown

Backport #12898 to release/v1.68.x

Warn when the StatsD client gives up connecting

When the StatsD connection ran out of connect retries it logged at debug
and stopped for the life of the JVM, so default customer logs showed no
sign that tracer metrics were never sent.

Warn instead, with the StatsD address, the number of attempts, and the
last error.

Keep retrying the first StatsD connection

When the DogStatsD pipe or port was missing at startup, the StatsD
connection retried 20 times, 10 seconds apart, then gave up for the life
of the JVM, so a DogStatsD that came up later was never used. In Azure App
Service this happens when the tracer starts before DogStatsD listens.

- Retry forever, doubling the delay from 10 seconds to a cap of 60
  seconds. Drop the jitter on retries so the delay stays within the cap.
- Warn on the first failure, and again at most every 5 minutes once the
  delay reaches the cap, so a lasting failure stays visible without debug
  logs. Log other attempts at debug, and log once at info when a
  connection succeeds after failures.

Recreate the StatsD client after writes keep failing

The StatsD client opens its named pipe once and never reopens it. After
the DogStatsD it was writing to went away, every write failed for the
life of the JVM, even once a new DogStatsD served the same pipe name. In
an Azure App Service overlapping restart, tracer metrics were still
missing 50 minutes after the old worker's DogStatsD exited.

- Track the client's own send failures, for named pipes only. They use
  a separate error handler, so errors reported through
  StatsDClient.error() are still logged and counted by handle() but
  never trigger a recreate. The UDP and Unix datagram clients send to
  their address on every write, and the Unix stream client reconnects
  itself, so recreating them would only drop queued metrics, for example
  while an overloaded agent fills the socket buffer.
- Errors more than 60 seconds apart start a new streak; a streak of at
  least 3 errors spanning at least 30 seconds triggers a recreate. The
  60 second gap is longer than the 30 second tracer health metrics
  flush, so a dead pipe keeps one streak going, while brief bursts and
  isolated timeouts under load do not trigger it.
- Recreate on the task scheduler, not the client's own thread: close the
  client and reconnect through the existing connect path, which retries
  with backoff if the pipe is still missing.
- Warn on recreate, rate limited to once every 5 minutes.

Open a new named pipe connection for each socket

NamedPipeSocketFactory kept one NamedPipeSocket per pipe and returned it
to every OkHttp connection (#3493). NamedPipeSocket ignores read timeouts,
so when OkHttp checks a pooled connection's health it blocks on an
unbounded pipe read, and every other agent request (traces, stats, remote
config, telemetry) waits behind it on the shared handle. On Azure
Functions with DD_TRACE_PIPE_NAME set, the tracer queued spans but never
sent or failed a trace upload until the JVM restarted.

Open a new pipe handle for each socket, as the .NET tracer does for each
request. #3493 shared the handle because a named pipe was thought to
allow only one client at a time; that change was untested on Windows, and
the .NET tracer's per-request pipe connections show the agent's pipe
server accepts several clients.

The agent's pipe servers create their next pipe instance only after a
client connects, so a request that opens the pipe in between fails with
"All pipe instances are busy". Like .NET's NamedPipeClientStream.Connect,
retry opening the pipe for up to 500 ms, the .NET tracer's default
DD_TRACE_PIPE_TIMEOUT_MS. Java reports a busy pipe, a missing pipe, and
other open failures with the same exception, so all of them are retried.

Don't reuse named pipe connections

NamedPipeSocket can't time out a read, so when OkHttp checks a pooled
connection's health before reusing it, the check can block forever on the
pipe. Give named-pipe clients a connection pool that keeps no idle
connections, so each request opens and closes its own pipe connection, as
the .NET tracer does. For named pipes this replaces the maxRunningRequests
connection pool; the dispatcher limits are unchanged.

Connect tracer StatsD over a named pipe without a port

When a DogStatsD named pipe was set and no port was configured,
DDAgentStatsDConnection left the port null, and building the client threw
NullPointerException on .port(port). Tracer health metrics and JMXFetch
JVM metrics were never sent over a pipe.

Use port 0 when a named pipe is set and no port is configured. The client
connects to the pipe and ignores the port; port 0 also selects the same
packet size and socket settings the tracer uses for Unix domain sockets.

Close named pipe connections after each exchange

Review follow-ups for named pipes:

- An empty connection pool did not stop reuse: OkHttp sends a retry after
  HTTP 408, or 503 with Retry-After: 0, on the same connection without
  returning it to the pool, and the health check before that reuse can
  block forever on the pipe. Send "Connection: close" on every HTTP/1.1
  exchange instead, which covers retries too. The named pipe client goes
  back to the usual connection pool, which never holds these connections.
- Resolve the agent host to one loopback address. The socket factory opens
  the pipe whatever the address, so with both IPv4 and IPv6 addresses
  OkHttp waited out the pipe connect timeout once per address.
- Only an IOException advances the StatsD write failure streak. The client
  also reports encoding errors, such as a metric that is too long, which a
  new client would not fix.

Test that named pipe retries open a new connection

OkHttp sends a retry after HTTP 408, or 503 with Retry-After: 0, on the
connection it already holds, and the health check before that reuse can
block forever on a named pipe. Check that both attempts send
"Connection: close" and each is the first request on its connection. A TCP
socket stands in for the pipe, so the tests run on any OS.

Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
(cherry picked from commit 17c6cd1)
@apiarian-datadog
apiarian-datadog requested review from a team as code owners November 5, 2026 17:33
@apiarian-datadog
apiarian-datadog requested review from khanayan123 and removed request for a team November 5, 2026 17:33
@apiarian-datadog
apiarian-datadog requested review from ygree and removed request for a team November 5, 2026 17:33
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-05T17:35:40.426566Z d11134e PR opened
🔒 Security Review ✅ Completed 2026-11-05T17:37:06.757327Z d11134e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@datadog-official datadog-official 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.

Bits Code Review: PASS

More details

Source inspection found no reportable defects in HTTP named-pipe connection handling or StatsD reconnection. Actual Windows named-pipe behavior remains unverified at runtime.

Was this helpful? React 👍 or 👎

Open Bits AI session

🤖 Bits Code Review · Commit d11134e

@dd-octo-sts

dd-octo-sts Bot commented Nov 5, 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.00 s 13.88 s [+0.3%; +1.6%] (maybe worse)
startup:insecure-bank:tracing:Agent 12.96 s 12.96 s [-0.9%; +0.8%] (no difference)
startup:petclinic:appsec:Agent 17.14 s 16.99 s [-0.1%; +1.8%] (no difference)
startup:petclinic:iast:Agent 16.96 s 17.04 s [-1.2%; +0.3%] (no difference)
startup:petclinic:profiling:Agent 16.71 s 16.89 s [-2.2%; -0.0%] (maybe better)
startup:petclinic:sca:Agent 17.07 s 16.99 s [-0.4%; +1.3%] (no difference)
startup:petclinic:tracing:Agent 16.10 s 16.27 s [-2.1%; -0.1%] (maybe better)

Commit: d11134ee · 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.

@datadog-official

datadog-official Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

🎯 Code Coverage (details)
• Patch Coverage: 14.86%
• Overall Coverage: 61.46% (+1.47%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: d11134e | Docs | Give us feedback!

@apiarian-datadog

Copy link
Copy Markdown
Author

Checked with a test app derived from our serverless Azure App Service windows extension test suite. Confrirmed that traces and trace stats now flow correctly.

@PerfectSlayer PerfectSlayer removed the comp: metrics Metrics label Nov 6, 2026

This branch has not been deployed

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

Labels

comp: core Tracer core type: bug fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants