Skip to content

SVLS-9418: check that the dogstatsd pipe is actually bound - #8874

Closed
apiarian-datadog wants to merge 9 commits into
masterfrom
aleksandr.pasechnik/svls-9418-fix-dogstatsd-health-check-with-named-pipes
Closed

apiarian-datadog wants to merge 9 commits into
masterfrom
aleksandr.pasechnik/svls-9418-fix-dogstatsd-health-check-with-named-pipes

Conversation

@apiarian-datadog

@apiarian-datadog apiarian-datadog commented Jul 7, 2026 •

Copy link
Copy Markdown

Summary of changes

Change the dogstatsd process management to require that the pipe is bound and the process is running.

Reason for change

In some Azure App Service restarts, the old app and its dogstatsd server is still running, with the dogstatsd holding on to the named pipe. The new dogstatsd fails to bind to the pipe and just stalls, doing nothing (datadog-agent patch, but we'd like to have coverage here in the tracer in addition to the dogstatsd patch).

Implementation details

Add a debounced process and pipe check.

Test coverage

Unit test. Will also test manually in an actual azure app service environment.

@apiarian-datadog
apiarian-datadog requested review from a team as code owners July 7, 2026 16:52

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 77fa65884f

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs Outdated
Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs
@dd-trace-dotnet-ci-bot

dd-trace-dotnet-ci-bot Bot commented Jul 7, 2026 •

Copy link
Copy Markdown

Execution-Time Benchmarks Report ⏱️

Execution-time results for samples comparing This PR (8874) and master.

✅ No regressions detected - check the details below

Full Metrics Comparison

FakeDbCommand

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration70.65 ± (70.71 - 70.96) ms71.48 ± (71.52 - 71.87) ms+1.2%✅⬆️
.NET Framework 4.8 - Bailout
duration75.00 ± (74.85 - 75.22) ms79.33 ± (78.98 - 79.53) ms+5.8%✅⬆️
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1090.32 ± (1092.01 - 1100.58) ms1088.07 ± (1088.78 - 1096.38) ms-0.2%✅
.NET Core 3.1 - Baseline
process.internal_duration_ms22.13 ± (22.09 - 22.16) ms22.48 ± (22.42 - 22.53) ms+1.6%✅⬆️
process.time_to_main_ms81.18 ± (81.01 - 81.35) ms84.27 ± (83.95 - 84.60) ms+3.8%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%✅
runtime.dotnet.mem.committed11.00 ± (11.00 - 11.01) MB11.04 ± (11.03 - 11.04) MB+0.3%✅⬆️
runtime.dotnet.threads.count12 ± (12 - 12)12 ± (12 - 12)+0.0%✅
.NET Core 3.1 - Bailout
process.internal_duration_ms22.06 ± (22.02 - 22.09) ms22.20 ± (22.16 - 22.23) ms+0.6%✅⬆️
process.time_to_main_ms82.64 ± (82.47 - 82.82) ms84.21 ± (83.95 - 84.48) ms+1.9%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%✅
runtime.dotnet.mem.committed11.02 ± (11.02 - 11.03) MB11.08 ± (11.07 - 11.08) MB+0.5%✅⬆️
runtime.dotnet.threads.count13 ± (13 - 13)13 ± (13 - 13)+0.0%✅
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms209.20 ± (208.33 - 210.08) ms209.82 ± (208.78 - 210.87) ms+0.3%✅⬆️
process.time_to_main_ms536.98 ± (535.71 - 538.25) ms538.20 ± (536.65 - 539.75) ms+0.2%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%✅
runtime.dotnet.mem.committed49.61 ± (49.58 - 49.63) MB49.65 ± (49.62 - 49.68) MB+0.1%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)-0.1%✅
.NET 6 - Baseline
process.internal_duration_ms21.05 ± (21.00 - 21.09) ms21.14 ± (21.09 - 21.20) ms+0.4%✅⬆️
process.time_to_main_ms73.33 ± (73.08 - 73.58) ms74.17 ± (73.89 - 74.44) ms+1.1%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%✅
runtime.dotnet.mem.committed10.73 ± (10.73 - 10.73) MB10.74 ± (10.74 - 10.74) MB+0.1%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%✅
.NET 6 - Bailout
process.internal_duration_ms20.76 ± (20.73 - 20.78) ms20.81 ± (20.79 - 20.84) ms+0.3%✅⬆️
process.time_to_main_ms72.12 ± (72.00 - 72.24) ms72.62 ± (72.47 - 72.76) ms+0.7%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%✅
runtime.dotnet.mem.committed10.85 ± (10.85 - 10.86) MB10.87 ± (10.87 - 10.87) MB+0.1%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%✅
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms374.03 ± (371.60 - 376.46) ms373.61 ± (371.46 - 375.75) ms-0.1%✅
process.time_to_main_ms545.66 ± (544.18 - 547.13) ms546.60 ± (545.18 - 548.01) ms+0.2%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%✅
runtime.dotnet.mem.committed50.61 ± (50.59 - 50.64) MB50.66 ± (50.63 - 50.68) MB+0.1%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)-0.1%✅
.NET 8 - Baseline
process.internal_duration_ms19.10 ± (19.07 - 19.14) ms19.23 ± (19.18 - 19.27) ms+0.6%✅⬆️
process.time_to_main_ms70.59 ± (70.44 - 70.75) ms71.51 ± (71.20 - 71.83) ms+1.3%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%✅
runtime.dotnet.mem.committed7.75 ± (7.75 - 7.76) MB7.76 ± (7.75 - 7.77) MB+0.1%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%✅
.NET 8 - Bailout
process.internal_duration_ms19.40 ± (19.36 - 19.45) ms19.41 ± (19.36 - 19.47) ms+0.0%✅⬆️
process.time_to_main_ms75.14 ± (74.86 - 75.42) ms75.24 ± (74.93 - 75.54) ms+0.1%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%✅
runtime.dotnet.mem.committed7.81 ± (7.80 - 7.81) MB7.82 ± (7.81 - 7.82) MB+0.1%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%✅
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms295.52 ± (293.08 - 297.96) ms298.42 ± (296.18 - 300.65) ms+1.0%✅⬆️
process.time_to_main_ms496.30 ± (495.13 - 497.47) ms492.69 ± (491.58 - 493.80) ms-0.7%✅
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%✅
runtime.dotnet.mem.committed38.11 ± (38.08 - 38.13) MB38.15 ± (38.12 - 38.18) MB+0.1%✅⬆️
runtime.dotnet.threads.count27 ± (27 - 27)27 ± (27 - 27)-0.7%✅

HttpMessageHandler

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration208.83 ± (208.79 - 209.67) ms212.61 ± (212.37 - 213.26) ms+1.8%✅⬆️
.NET Framework 4.8 - Bailout
duration211.31 ± (211.19 - 212.15) ms215.61 ± (215.33 - 216.23) ms+2.0%✅⬆️
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1246.32 ± (1245.34 - 1251.00) ms1265.24 ± (1263.44 - 1269.23) ms+1.5%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms199.22 ± (198.78 - 199.67) ms203.05 ± (202.60 - 203.50) ms+1.9%✅⬆️
process.time_to_main_ms87.28 ± (86.96 - 87.59) ms89.66 ± (89.38 - 89.95) ms+2.7%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%✅
runtime.dotnet.mem.committed16.16 ± (16.13 - 16.18) MB16.15 ± (16.13 - 16.18) MB-0.0%✅
runtime.dotnet.threads.count20 ± (20 - 20)20 ± (20 - 20)+1.7%✅⬆️
.NET Core 3.1 - Bailout
process.internal_duration_ms198.83 ± (198.48 - 199.17) ms203.55 ± (203.14 - 203.96) ms+2.4%✅⬆️
process.time_to_main_ms88.38 ± (88.19 - 88.57) ms91.35 ± (91.09 - 91.61) ms+3.4%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%✅
runtime.dotnet.mem.committed16.14 ± (16.12 - 16.16) MB16.21 ± (16.19 - 16.23) MB+0.5%✅⬆️
runtime.dotnet.threads.count21 ± (20 - 21)21 ± (21 - 21)+1.2%✅⬆️
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms393.05 ± (391.86 - 394.25) ms398.90 ± (397.63 - 400.16) ms+1.5%✅⬆️
process.time_to_main_ms560.46 ± (559.12 - 561.81) ms565.02 ± (563.61 - 566.43) ms+0.8%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%✅
runtime.dotnet.mem.committed59.33 ± (59.12 - 59.54) MB59.89 ± (59.81 - 59.96) MB+0.9%✅⬆️
runtime.dotnet.threads.count30 ± (30 - 30)30 ± (30 - 30)+0.4%✅⬆️
.NET 6 - Baseline
process.internal_duration_ms203.47 ± (203.04 - 203.91) ms208.50 ± (208.01 - 209.00) ms+2.5%✅⬆️
process.time_to_main_ms76.06 ± (75.76 - 76.36) ms78.69 ± (78.41 - 78.97) ms+3.5%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%✅
runtime.dotnet.mem.committed16.39 ± (16.37 - 16.41) MB16.31 ± (16.29 - 16.33) MB-0.5%✅
runtime.dotnet.threads.count19 ± (19 - 19)20 ± (19 - 20)+1.1%✅⬆️
.NET 6 - Bailout
process.internal_duration_ms204.47 ± (204.14 - 204.80) ms209.07 ± (208.52 - 209.62) ms+2.2%✅⬆️
process.time_to_main_ms77.45 ± (77.24 - 77.67) ms80.26 ± (79.95 - 80.56) ms+3.6%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%✅
runtime.dotnet.mem.committed16.49 ± (16.47 - 16.52) MB16.34 ± (16.31 - 16.36) MB-0.9%✅
runtime.dotnet.threads.count20 ± (20 - 20)21 ± (20 - 21)+1.0%✅⬆️
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms585.71 ± (583.44 - 587.98) ms581.41 ± (578.91 - 583.91) ms-0.7%✅
process.time_to_main_ms574.01 ± (572.86 - 575.16) ms581.66 ± (580.48 - 582.84) ms+1.3%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%✅
runtime.dotnet.mem.committed61.63 ± (61.54 - 61.72) MB61.61 ± (61.53 - 61.70) MB-0.0%✅
runtime.dotnet.threads.count31 ± (31 - 31)31 ± (31 - 31)-0.1%✅
.NET 8 - Baseline
process.internal_duration_ms205.83 ± (205.40 - 206.27) ms207.57 ± (207.10 - 208.04) ms+0.8%✅⬆️
process.time_to_main_ms76.82 ± (76.54 - 77.09) ms77.92 ± (77.66 - 78.18) ms+1.4%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%✅
runtime.dotnet.mem.committed11.78 ± (11.76 - 11.80) MB11.78 ± (11.76 - 11.80) MB-0.0%✅
runtime.dotnet.threads.count19 ± (19 - 19)19 ± (19 - 19)+1.0%✅⬆️
.NET 8 - Bailout
process.internal_duration_ms204.94 ± (204.45 - 205.42) ms206.27 ± (205.86 - 206.68) ms+0.7%✅⬆️
process.time_to_main_ms77.99 ± (77.75 - 78.23) ms78.77 ± (78.54 - 79.00) ms+1.0%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%✅
runtime.dotnet.mem.committed11.85 ± (11.83 - 11.87) MB11.75 ± (11.73 - 11.77) MB-0.8%✅
runtime.dotnet.threads.count20 ± (20 - 20)20 ± (20 - 20)-0.3%✅
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms511.50 ± (506.96 - 516.03) ms521.63 ± (514.96 - 528.30) ms+2.0%✅⬆️
process.time_to_main_ms526.67 ± (525.78 - 527.56) ms532.95 ± (532.00 - 533.90) ms+1.2%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%✅
runtime.dotnet.mem.committed51.46 ± (51.40 - 51.51) MB51.53 ± (51.46 - 51.60) MB+0.1%✅⬆️
runtime.dotnet.threads.count30 ± (30 - 30)30 ± (30 - 30)-0.0%✅
Comparison explanation

Execution-time benchmarks measure the whole time it takes to execute a program, and are intended to measure the one-off costs. Cases where the execution time results for the PR are worse than latest master results are highlighted in **red**. The following thresholds were used for comparing the execution times:

  • Welch test with statistical test for significance of 5%
  • Only results indicating a difference greater than 5% and 5 ms are considered.

Note that these results are based on a single point-in-time result for each branch. For full results, see the dashboard.

Graphs show the p99 interval based on the mean and StdDev of the test run, as well as the mean value of the run (shown as a diamond below the graph).

Duration charts
FakeDbCommand (.NET Framework 4.8)
gantt
    title Execution time (ms) FakeDbCommand (.NET Framework 4.8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8874) - mean (72ms)  : 69, 74
    master - mean (71ms)  : 69, 73

    section Bailout
    This PR (8874) - mean (79ms)  : 75, 83
    master - mean (75ms)  : 73, 77

    section CallTarget+Inlining+NGEN
    This PR (8874) - mean (1,093ms)  : 1038, 1147
    master - mean (1,096ms)  : 1034, 1159

Loading
FakeDbCommand (.NET Core 3.1)
gantt
    title Execution time (ms) FakeDbCommand (.NET Core 3.1)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8874) - mean (114ms)  : 108, 120
    master - mean (110ms)  : 107, 113

    section Bailout
    This PR (8874) - mean (114ms)  : 108, 120
    master - mean (111ms)  : 108, 114

    section CallTarget+Inlining+NGEN
    This PR (8874) - mean (784ms)  : 760, 808
    master - mean (783ms)  : 755, 810

Loading
FakeDbCommand (.NET 6)
gantt
    title Execution time (ms) FakeDbCommand (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8874) - mean (102ms)  : 96, 109
    master - mean (101ms)  : 95, 107

    section Bailout
    This PR (8874) - mean (99ms)  : 97, 102
    master - mean (99ms)  : 97, 100

    section CallTarget+Inlining+NGEN
    This PR (8874) - mean (952ms)  : 913, 992
    master - mean (951ms)  : 910, 991

Loading
FakeDbCommand (.NET 8)
gantt
    title Execution time (ms) FakeDbCommand (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8874) - mean (98ms)  : 92, 105
    master - mean (97ms)  : 93, 100

    section Bailout
    This PR (8874) - mean (102ms)  : 95, 109
    master - mean (102ms)  : 97, 107

    section CallTarget+Inlining+NGEN
    This PR (8874) - mean (821ms)  : 785, 856
    master - mean (820ms)  : 783, 856

Loading
HttpMessageHandler (.NET Framework 4.8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET Framework 4.8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8874) - mean (213ms)  : 208, 217
    master - mean (209ms)  : 204, 215

    section Bailout
    This PR (8874) - mean (216ms)  : 211, 220
    master - mean (212ms)  : 207, 216

    section CallTarget+Inlining+NGEN
    This PR (8874) - mean (1,266ms)  : 1227, 1305
    master - mean (1,248ms)  : 1208, 1288

Loading
HttpMessageHandler (.NET Core 3.1)
gantt
    title Execution time (ms) HttpMessageHandler (.NET Core 3.1)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8874) - mean (303ms)  : 296, 311
    master - mean (297ms)  : 289, 304

    section Bailout
    This PR (8874) - mean (305ms)  : 298, 311
    master - mean (297ms)  : 292, 301

    section CallTarget+Inlining+NGEN
    This PR (8874) - mean (1,006ms)  : 982, 1030
    master - mean (997ms)  : 977, 1017

Loading
HttpMessageHandler (.NET 6)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8874) - mean (297ms)  : 291, 304
    master - mean (289ms)  : 282, 295

    section Bailout
    This PR (8874) - mean (299ms)  : 291, 308
    master - mean (290ms)  : 285, 295

    section CallTarget+Inlining+NGEN
    This PR (8874) - mean (1,200ms)  : 1156, 1244
    master - mean (1,191ms)  : 1140, 1243

Loading
HttpMessageHandler (.NET 8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8874) - mean (296ms)  : 288, 304
    master - mean (293ms)  : 287, 300

    section Bailout
    This PR (8874) - mean (295ms)  : 291, 300
    master - mean (293ms)  : 286, 300

    section CallTarget+Inlining+NGEN
    This PR (8874) - mean (1,089ms)  : 994, 1184
    master - mean (1,073ms)  : 999, 1147

Loading

@pr-commenter

pr-commenter Bot commented Jul 7, 2026 •

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-07-24 17:38:04

Comparing candidate commit d51fa95 in PR branch aleksandr.pasechnik/svls-9418-fix-dogstatsd-health-check-with-named-pipes with baseline commit 87461a4 in branch master.

📊 Benchmarking dashboard

Found 0 performance improvements and 2 performance regressions! Performance is the same for 70 metrics, 0 unstable metrics, 60 known flaky benchmarks, 66 flaky benchmarks without significant changes.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

scenario:Benchmarks.Trace.DbCommandBenchmark.ExecuteNonQuery net472

  • 🟥 throughput [-26785.575op/s; -23833.832op/s] or [-7.544%; -6.713%]

scenario:Benchmarks.Trace.HttpClientBenchmark.SendAsync net472

  • 🟥 throughput [-4985.508op/s; -4696.603op/s] or [-5.691%; -5.362%]

Known flaky benchmarks

These benchmarks are marked as flaky and will not trigger a failure. Modify FLAKY_BENCHMARKS_REGEX to control which benchmarks are marked as flaky.

scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild net472

  • 🟥 throughput [-9510.326op/s; -8771.487op/s] or [-11.276%; -10.400%]

scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild netcoreapp3.1

  • 🟥 throughput [-9847.167op/s; -8647.002op/s] or [-10.012%; -8.792%]

scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces net472

  • 🟥 execution_time [+316.187ms; +323.960ms] or [+156.903%; +160.761%]
  • 🟥 throughput [-42.235op/s; -38.607op/s] or [-7.599%; -6.946%]

scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces net6.0

  • 🟥 execution_time [+379.651ms; +382.436ms] or [+299.948%; +302.148%]
  • 🟩 throughput [+90.309op/s; +95.009op/s] or [+11.907%; +12.527%]

scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces netcoreapp3.1

  • 🟥 execution_time [+391.695ms; +395.738ms] or [+346.635%; +350.213%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody net472

  • 🟥 allocated_mem [+1.308KB; +1.308KB] or [+27.528%; +27.540%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody net6.0

  • 🟥 allocated_mem [+471 bytes; +472 bytes] or [+9.976%; +9.987%]
  • 🟩 execution_time [-16.102ms; -11.938ms] or [-7.520%; -5.575%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleMoreComplexBody netcoreapp3.1

  • 🟥 allocated_mem [+1.272KB; +1.272KB] or [+27.500%; +27.510%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody net472

  • 🟥 allocated_mem [+1.307KB; +1.307KB] or [+105.743%; +105.758%]
  • 🟥 throughput [-265653.349op/s; -261005.692op/s] or [-27.125%; -26.650%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody net6.0

  • 🟥 allocated_mem [+471 bytes; +472 bytes] or [+38.557%; +38.566%]
  • 🟩 execution_time [-26.878ms; -22.010ms] or [-11.986%; -9.815%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.AllCycleSimpleBody netcoreapp3.1

  • 🟥 allocated_mem [+1.272KB; +1.272KB] or [+105.288%; +105.304%]
  • 🟥 throughput [-154855.908op/s; -138607.407op/s] or [-22.250%; -19.915%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorMoreComplexBody net6.0

  • 🟩 throughput [+8213.552op/s; +11253.444op/s] or [+5.226%; +7.160%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorMoreComplexBody netcoreapp3.1

  • 🟩 throughput [+8606.327op/s; +11274.249op/s] or [+6.856%; +8.981%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody net6.0

  • 🟩 throughput [+363554.515op/s; +494782.606op/s] or [+12.122%; +16.498%]

scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody netcoreapp3.1

  • 🟩 execution_time [-19.337ms; -15.001ms] or [-8.914%; -6.915%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeArgs net472

  • 🟥 execution_time [+299.489ms; +300.459ms] or [+149.644%; +150.129%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeArgs net6.0

  • 🟥 execution_time [+299.916ms; +303.892ms] or [+151.248%; +153.253%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeArgs netcoreapp3.1

  • 🟥 execution_time [+299.608ms; +302.001ms] or [+150.919%; +152.125%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs net472

  • 🟥 execution_time [+296.285ms; +297.374ms] or [+145.523%; +146.058%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs net6.0

  • 🟥 execution_time [+288.166ms; +290.181ms] or [+140.874%; +141.859%]

scenario:Benchmarks.Trace.Asm.AppSecEncoderBenchmark.EncodeLegacyArgs netcoreapp3.1

  • 🟥 execution_time [+300.083ms; +302.120ms] or [+149.981%; +150.999%]

scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack net6.0

  • 🟥 execution_time [+24.523µs; +48.522µs] or [+7.829%; +15.491%]
  • 🟥 throughput [-447.866op/s; -246.427op/s] or [-13.961%; -7.682%]

scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest net472

  • 🟥 execution_time [+300.356ms; +301.145ms] or [+149.909%; +150.302%]

scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest net6.0

  • unstable execution_time [+321.764ms; +394.219ms] or [+349.610%; +428.335%]
  • 🟩 throughput [+734.532op/s; +930.478op/s] or [+6.036%; +7.646%]

scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest netcoreapp3.1

  • 🟥 execution_time [+369.964ms; +375.369ms] or [+280.910%; +285.014%]

scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net472

  • 🟥 allocated_mem [+2.848KB; +2.853KB] or [+5.059%; +5.069%]
  • unstable execution_time [+343.584ms; +402.278ms] or [+157.977%; +184.964%]
  • 🟥 throughput [-544.657op/s; -494.836op/s] or [-49.351%; -44.837%]

scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net6.0

  • unstable execution_time [+206.974ms; +340.228ms] or [+88.203%; +144.991%]
  • 🟥 throughput [-665.801op/s; -582.374op/s] or [-44.409%; -38.844%]

scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces netcoreapp3.1

  • 🟥 execution_time [+341.392ms; +349.526ms] or [+204.192%; +209.057%]
  • 🟥 throughput [-393.582op/s; -354.974op/s] or [-27.405%; -24.716%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice net6.0

  • 🟩 execution_time [-178.006µs; -133.823µs] or [-9.017%; -6.779%]
  • 🟩 throughput [+38.537op/s; +50.126op/s] or [+7.608%; +9.895%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearch net472

  • 🟥 execution_time [+302.719ms; +304.719ms] or [+152.444%; +153.450%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearch net6.0

  • 🟥 execution_time [+302.189ms; +303.447ms] or [+151.428%; +152.058%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearch netcoreapp3.1

  • 🟥 execution_time [+300.855ms; +304.003ms] or [+151.137%; +152.718%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync net472

  • 🟥 execution_time [+303.132ms; +305.541ms] or [+152.223%; +153.432%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync net6.0

  • 🟥 execution_time [+297.915ms; +300.604ms] or [+147.306%; +148.635%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync netcoreapp3.1

  • 🟥 execution_time [+301.527ms; +305.596ms] or [+152.827%; +154.889%]

scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync net472

  • 🟥 execution_time [+301.165ms; +302.659ms] or [+151.158%; +151.908%]

scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync net6.0

  • 🟥 execution_time [+297.637ms; +300.323ms] or [+148.345%; +149.683%]
  • 🟩 throughput [+45532.222op/s; +54313.969op/s] or [+9.041%; +10.785%]

scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync netcoreapp3.1

  • 🟥 execution_time [+299.650ms; +302.898ms] or [+149.073%; +150.689%]

scenario:Benchmarks.Trace.ILoggerBenchmark.EnrichedLog net6.0

  • 🟩 execution_time [-17.260ms; -13.620ms] or [-8.026%; -6.333%]
  • 🟩 throughput [+24506.222op/s; +31008.973op/s] or [+6.723%; +8.507%]

scenario:Benchmarks.Trace.ILoggerBenchmark.EnrichedLog netcoreapp3.1

  • 🟩 throughput [+13734.606op/s; +19780.027op/s] or [+5.013%; +7.220%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark net472

  • unstable execution_time [+16.848µs; +61.538µs] or [+4.162%; +15.200%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark net6.0

  • 🟩 allocated_mem [-19.880KB; -19.858KB] or [-7.252%; -7.244%]
  • unstable execution_time [-21.625µs; +40.228µs] or [-4.274%; +7.951%]
  • unstable throughput [-136.384op/s; +80.020op/s] or [-6.806%; +3.993%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatAspectBenchmark netcoreapp3.1

  • 🟩 allocated_mem [-17.227KB; -17.207KB] or [-6.280%; -6.273%]
  • unstable execution_time [-53.726µs; +7.977µs] or [-9.310%; +1.382%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark net6.0

  • unstable execution_time [+7.245µs; +11.686µs] or [+17.125%; +27.621%]
  • 🟥 throughput [-5208.862op/s; -3369.610op/s] or [-21.928%; -14.185%]

scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark netcoreapp3.1

  • unstable execution_time [-15.138µs; -7.660µs] or [-23.487%; -11.884%]
  • 🟩 throughput [+1963.758op/s; +3568.017op/s] or [+12.048%; +21.891%]

scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog net472

  • 🟥 execution_time [+301.934ms; +303.109ms] or [+152.614%; +153.208%]

scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog net6.0

  • 🟥 execution_time [+302.974ms; +305.094ms] or [+154.213%; +155.292%]

scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog netcoreapp3.1

  • 🟥 execution_time [+300.627ms; +303.003ms] or [+150.501%; +151.691%]

scenario:Benchmarks.Trace.RedisBenchmark.SendReceive net472

  • 🟥 throughput [-21337.088op/s; -19798.518op/s] or [-5.907%; -5.481%]

scenario:Benchmarks.Trace.SerilogBenchmark.EnrichedLog net472

  • 🟥 execution_time [+296.660ms; +299.564ms] or [+147.858%; +149.306%]

scenario:Benchmarks.Trace.SerilogBenchmark.EnrichedLog net6.0

  • 🟥 execution_time [+302.080ms; +303.803ms] or [+151.690%; +152.555%]

scenario:Benchmarks.Trace.SerilogBenchmark.EnrichedLog netcoreapp3.1

  • 🟥 execution_time [+302.785ms; +305.315ms] or [+153.553%; +154.836%]

scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore net472

  • 🟥 execution_time [+299.194ms; +300.393ms] or [+149.239%; +149.837%]
  • 🟩 throughput [+66027364.537op/s; +66392778.914op/s] or [+48.085%; +48.351%]

scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore net6.0

  • unstable execution_time [+343.596ms; +402.166ms] or [+427.323%; +500.164%]

scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore netcoreapp3.1

  • 🟥 execution_time [+299.195ms; +300.234ms] or [+149.232%; +149.750%]
  • 🟩 throughput [+17756713.935op/s; +18730046.870op/s] or [+7.865%; +8.296%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishScope net6.0

  • 🟩 throughput [+97860.389op/s; +108426.817op/s] or [+9.137%; +10.123%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishScope netcoreapp3.1

  • 🟩 throughput [+48309.827op/s; +68196.326op/s] or [+5.592%; +7.894%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan netcoreapp3.1

  • 🟩 throughput [+73863.008op/s; +81663.330op/s] or [+7.336%; +8.110%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes net6.0

  • 🟩 throughput [+46209.310op/s; +52043.024op/s] or [+8.391%; +9.450%]

scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin net6.0

  • 🟩 throughput [+63147.633op/s; +89237.666op/s] or [+7.055%; +9.970%]

Known flaky benchmarks without significant changes:

  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_AddEvent_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_GetContext_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetAttributes_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_SetStatus_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.ActivityBenchmark.StartSpan_UpdateName_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_AddEvent_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_GetContext_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_RecordException_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetAttributes_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_SetStatus_Sampled netcoreapp3.1
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled net472
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled net6.0
  • scenario:Benchmarks.OpenTelemetry.InstrumentedApi.Trace.TelemetrySpanBenchmark.StartSpan_UpdateName_Sampled netcoreapp3.1
  • scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild net6.0
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorMoreComplexBody net472
  • scenario:Benchmarks.Trace.Asm.AppSecBodyBenchmark.ObjectExtractorSimpleBody net472
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark net472
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark net6.0
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark netcoreapp3.1
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack net472
  • scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack netcoreapp3.1
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice net472
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice net6.0
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice netcoreapp3.1
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool net472
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool net6.0
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool netcoreapp3.1
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice net472
  • scenario:Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice netcoreapp3.1
  • scenario:Benchmarks.Trace.ILoggerBenchmark.EnrichedLog net472
  • scenario:Benchmarks.Trace.Iast.StringAspectsBenchmark.StringConcatBenchmark net472
  • scenario:Benchmarks.Trace.RedisBenchmark.SendReceive net6.0
  • scenario:Benchmarks.Trace.RedisBenchmark.SendReceive netcoreapp3.1
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishScope net472
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan net472
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan net6.0
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes net472
  • scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes netcoreapp3.1
  • scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin net472
  • scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin netcoreapp3.1

@apiarian-datadog

Copy link
Copy Markdown
Author

validated with https://github.com/DataDog/serverless-examples/pull/205 . works in isolation and in combination.

Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs
Comment thread docs/development/SLES-2913-AzureAppServiceDogStatsdPipeRecovery.md Outdated
Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs Outdated

Copilot AI 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.

Pull request overview

Updates the Azure App Service child-process management logic (AgentProcessManager) to treat DogStatsD as healthy only when its named pipe is actually bound (for pipe-only configurations), helping recovery from overlapped restarts where a “running-but-broken” dogstatsd.exe can persist.

Changes:

  • Add a pipe-bound–aware (and debounced) health check path for pipe-only DogStatsD scenarios.
  • Add unit tests for the new health evaluation/debounce behavior and for DD_DOGSTATSD_PORT=0 + pipe-name config resolving to Named Pipe.
  • Add a manual verification guide for reproducing/validating recovery in Azure App Service.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
tracer/src/Datadog.Trace/AgentProcessManager.cs Adds pipe-only “require bound pipe” health semantics, debounced health checks, and stale-process kill-before-restart logic.
tracer/test/Datadog.Trace.Tests/Agent/AgentProcessManagerTests.cs Unit tests for new health evaluation and debounce arithmetic/threshold behavior.
tracer/test/Datadog.Trace.Tests/Configuration/ExporterSettingsTests.cs Ensures DD_DOGSTATSD_PORT=0 + pipe name resolves to Named Pipe transport (enabling pipe-only behavior).
docs/development/SLES-2913-AzureAppServiceDogStatsdPipeRecovery.md Manual validation steps for Azure App Service recovery behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs
Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs Outdated
@apiarian-datadog
apiarian-datadog force-pushed the aleksandr.pasechnik/svls-9418-fix-dogstatsd-health-check-with-named-pipes branch from 2043056 to 79d561f Compare July 15, 2026 19:24

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs Outdated
Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs
@apiarian-datadog apiarian-datadog changed the title SVLS-9418: check dogstatd is running AND pipe is bound SVLS-9418: check that the dogstatsd pipe is actually bound Jul 15, 2026
@apiarian-datadog
apiarian-datadog requested a review from Copilot July 15, 2026 21:18

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread tracer/test/Datadog.Trace.Tests/Agent/AgentProcessManagerTests.cs
Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs Outdated
Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs Outdated
@apiarian-datadog
apiarian-datadog force-pushed the aleksandr.pasechnik/svls-9418-fix-dogstatsd-health-check-with-named-pipes branch from fc34b90 to aaccf99 Compare July 22, 2026 13:51
await Task.Delay((int)delay).ConfigureAwait(false);

if (metadata.ProcessIsHealthy())
if (metadata.IsHealthyDebounced())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could you help me understand the timing here?

If I understand correctly the intention here is basically answering whether this AppDomain where this is running should start the DogStatsD process.

If DogStatsD is running but its pipe is not yet bound line 209 will return true
Waits 50ms
Checks here and if it is not bound again, it returns false

So I think this is saying that we only will ever need 50ms to bind a pipe otherwise we will start multiple DogStatsD processes across multiple AppDomains?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

the check here is in case some other app domain owns a dogstatsd process. if that domain owns it, it's either a peer that is free to do so or a previous instance that is about to shut down anyway.

if it is a healthy peer, we give it a little over 3 seconds (in steps of 50ms, 87ms, 153ms, 267ms, etc) to prove that it's still healthy. if so, the process state is kept as healthy and we move on.

if it is a dogstatsd that gets cleared out in those 3 seconds, we assume that we are recovering from some kind of previous shutdown and the dogstatsd we saw was a blip and we should be starting a new one.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

(actually, it's not 3 seconds, it's more like 1.8 since the last delay never hits. off by one here, may tweak the looping condition later.)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we may be discussing different cases?

My thought process here is that we have a multi AppDomain startup race with that 50ms pipe check now.

If AppDomain A's DogStatsD process is running but takes longer than 50ms to bind its pipe AppDomain B can classify it as unhealthy and start a second process. One of those processes will likely get the pipe ownership.

Is this correct? If so is this a known limitation

I guess if so, there should follow up with a fix, but I'll approve to unblock the customer fix you are trying to get out. I would recommend a second review as this

@apiarian-datadog apiarian-datadog Jul 23, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

ah, ok, i think i understand now. there is in fact a race here, but it's a pretty low-risk one, i think.

there was no real cross-runner coordination, it was always based on os observations (one for checking the process by name and the other for checking if the named pipe is bound). our change prefers the pipe check over the process check, so it is possible for two process managers to both see that nothing is bound to the pipe and then try to launch the process.

however, this manager only runs in windows azure app service site extensions, and this new behavior only activates when we expect that the metrics transport is the named pipe. in all other scenarios we go back to the original pipe-bound || program-running behavior.

in azure app service extensions each app gets its own unique named pipe at install time (some day soon it will be a unique named pipe at runtime, too, i think). so there's no cross-app contention here, only limited within a single app.

and as you say, the change is self-correcting since only one of those dogstatsd processes will be able to bind to the unique named pipe. the others will fail to bind, and stop mattering since they don't bind to the pipe. we've got a change in the dogstatsd agent that will actually cause them to shut down when it cannot bind to a pipe, but that's a month and a half out at least in the agent release cycle.

we could explore some kind of system-wide lock on the dogstatsd, but that seems like too much extra complexity.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

and this new behavior only activates when we expect that the metrics transport is the named pipe

But the metrics transport is always named pipes in the AAS extension? 🤔

we've got a change in the dogstatsd agent that will actually cause them to shut down when it cannot bind to a pipe, but that's a month and a half out at least in the agent release cycle.

I mean, my inclination is to drop all the complexity here and just wait for that fix tbh, given (AFAICT) the only impact is leaking zombie dogstatsd instances, which seems like a low impact? 🤷‍♂️

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

unfortunately the zombie dogstatsd instances currently result in memory leaks in csharp: DataDog/dogstatsd-csharp-client#231 and it's going to take a month and a half for the agent changes to be released. we were hoping to get this patch out sooner so that we can get the extension in a better state.

@bouwkast bouwkast left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think there is a potential startup race condition that this creates where multiple DogStatsD processes can be started.
Given that this seems better than just having the process be broken maybe this is the more desirable state?

Regardless I would recommend a second approval before merging as this changes some long standing process management behavior.

await Task.Delay((int)delay).ConfigureAwait(false);

if (metadata.ProcessIsHealthy())
if (metadata.IsHealthyDebounced())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we may be discussing different cases?

My thought process here is that we have a multi AppDomain startup race with that 50ms pipe check now.

If AppDomain A's DogStatsD process is running but takes longer than 50ms to bind its pipe AppDomain B can classify it as unhealthy and start a second process. One of those processes will likely get the pipe ownership.

Is this correct? If so is this a known limitation

I guess if so, there should follow up with a fix, but I'll approve to unblock the customer fix you are trying to get out. I would recommend a second review as this

@andrewlock andrewlock left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Tbh, I'm not sure it's worth doing this work, adding a bunch of circuitous retrying when the root cause is being fixed upstream 🤷‍♂️ Either way, we can simplify the logic, because the AgentProcessManager is only called with named pipes AFAIK?

Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs Outdated
Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs Outdated
Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs Outdated
Comment on lines +127 to +128
// A pipe-only dogstatsd (named pipe transport selected) can stay alive but fully
// non-functional after failing to bind its pipe, so process liveness is not a

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems like a bug in dogstatsd? 🤔 Given we ship the tracer and dogstatsd together, can we make the fix in dogstatsd instead, which would remove all this complexity and heuristics?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

we are doing that, but it's going to take a month and a half to get that change released. this lets us cover the issue from both sides (there's a third side, actually: DataDog/dogstatsd-csharp-client#231 )

// non-functional after failing to bind its pipe, so process liveness is not a
// reliable health signal. Resolve the transport from configuration rather than
// re-deriving it, so this matches whatever the metrics client will actually use.
var exporterSettings = new ExporterSettings(GlobalConfigurationSource.Instance, File.Exists, NullConfigurationTelemetry.Instance);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is a whole bunch of duplicated work to be doing in the startup path - is it really necessary? Because AFAIK, AgentProcessManager is only used in the AAS extension, and we only support named pipes there? Could we not simplify a whole lot of this logic?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

i suppose we could add an assertion that we're running with a named pipe dogstatsd and then simplify the rest of the logic.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

out of the box the extension is configured to run with named pipes and disable the udp port. but it seems like there's no guarantee that a customer won't override it. so i think we do still need to err on the side of being ready for named pipes not to be required.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would strongly prefer that tbh, seeing as far as I know, only named pipes is supported

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

wait, prefer which? not allow customers to override the configuration at all?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just don't support it - if customers override the config they'll be broken anyway, so we're doing a bunch of extra work in the startup hotpath here to try to work around a scenario that's already broken 🤔

Tbh, my preference is probably just to wait for the dogstatsd fix because it seems like that avoids all the extra work here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

not supporting it would be a breaking change, wouldn't it? and it seems possible that a customer would disable named pipes and, say, manually manage unique ports for their colocated dogstatsd instances.

and a wait for the dogstatsd puts us back a month waiting for the agent release cycle. maybe that's okay given the csharp client updates, but we'd need to check with support.

await Task.Delay((int)delay).ConfigureAwait(false);

if (metadata.ProcessIsHealthy())
if (metadata.IsHealthyDebounced())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

and this new behavior only activates when we expect that the metrics transport is the named pipe

But the metrics transport is always named pipes in the AAS extension? 🤔

we've got a change in the dogstatsd agent that will actually cause them to shut down when it cannot bind to a pipe, but that's a month and a half out at least in the agent release cycle.

I mean, my inclination is to drop all the complexity here and just wait for that fix tbh, given (AFAICT) the only impact is leaking zombie dogstatsd instances, which seems like a low impact? 🤷‍♂️

Comment thread tracer/src/Datadog.Trace/AgentProcessManager.cs Outdated
apiarian-datadog added a commit to DataDog/dogstatsd-csharp-client that referenced this pull request Jul 24, 2026
…ool (#231)

`dogstatsd` in Azure App Services with the Windows Extension sometimes
fail to correctly get a named pipe. This can happen when the app is
hot-reloaded and the previous incarnation of `dogstatsd` hasn't released
the named pipe when the new `dogstatsd` is starting up. This causes the
named pipe writes from the dogstatsd client to fail (permanently, until
the app is restarted again).

These failures cause a steady growth of threads as the telemetry timer
spawns one every 10 seconds to try to write client telemetry, and the 2
second timeout for each of the 11 stats that the telemetry wants to send
compounds to 22 seconds of work. Forever.

This change turns the telemetry timer into one that reschedules itself
after every round, instead of spawning one every 10 seconds. It also
adds a cooldown for sending telemetry, so that if there's a failure to
send, we back off for a bit before trying to send any more.

This is one of a few different patches to address this issue. We also
have DataDog/dd-trace-dotnet#8874 for the app
service extension process manager and
DataDog/datadog-agent#53328 for dogstatsd.
@apiarian-datadog
apiarian-datadog force-pushed the aleksandr.pasechnik/svls-9418-fix-dogstatsd-health-check-with-named-pipes branch from aaccf99 to b2f2881 Compare July 24, 2026 15:44

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

tracer/test/Datadog.Trace.Tests/Agent/AgentProcessManagerTests.cs:7

  • AgentProcessManager is declared in the Datadog.Trace namespace, but this test file doesn't import it or fully-qualify the type, so it won't compile unless a project-wide global using exists (none found in tracer/test/Datadog.Trace.Tests/GlobalUsings.cs). Add using Datadog.Trace; (or fully-qualify Datadog.Trace.AgentProcessManager).
using FluentAssertions;
using Xunit;

@apiarian-datadog

Copy link
Copy Markdown
Author

looks like this isn't so urgent that we cannot wait for the dogstatsd change to be released. going to close this pr for now.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants