Skip to content

Add Multi-Azure Function Unique Named Pipe Configuration#8164

Open
Lewis-E wants to merge 42 commits intomasterfrom
lewis/SVLS-8244/add-multifunction-named-pipes
Open

Add Multi-Azure Function Unique Named Pipe Configuration#8164
Lewis-E wants to merge 42 commits intomasterfrom
lewis/SVLS-8244/add-multifunction-named-pipes

Conversation

@Lewis-E
Copy link
Copy Markdown

@Lewis-E Lewis-E commented Feb 5, 2026

Summary of changes

Reason for change

  • Improve support for running multiple Azure Functions in the same hosting plan. [jira]

Implementation details

For Azure Functions, the tracer loads first, then the compat layer, and then the compat layer launches a rust binary to act as the agent. Additionally, the client may or may not be using the DogstatsD client, and it is also possible to have the compat layer and DogstatsD in place without the tracer. We need to coordinate named pipes across all three/four of these codebases, noting that the tracer will use DogstatsD's pipe for runtime metrics if available.

Given this order of operations, and that we want this to work with immutable configuration, the tracer should set pipe names for the compatibility layer via instrumentation if the tracer is present.

  1. Tracer loads -- pipe name is generated
  2. We check the presence of the compat layer + windows configs (using undocumented envvar DD_SERVERLESS_COMPAT_BINARY_PATH if necessary to adapt to Azure changes) to see if named pipes can be used or if we should rely on TCP
  3. Compat layer loads
  4. Tracer uses instrumentation to force the compat layer to generate the pipe names ,
  5. Compat layer passes the name to the mini-agent tracer and dogstatsd .
Scenario Pipe generated? Transport Result
New tracer + no compat at all No (binary not found) TCP Works
New tracer + old compat (< 1.4) No (version check fails) TCP Works
New tracer + new compat (≥ 1.4) Yes Named pipe Works (integration coordinates)
New tracer + dev compat (0.0.0) Yes Named pipe Works (integration coordinates)
Old tracer + no compat No TCP Works
Old tracer + old compat (< 1.4) No TCP Works (neither side knows about pipes)
Old tracer + new compat (≥ 1.4) No TCP Broken — tracer sends via TCP, but compat binary listens on pipe only. Requires more serverless-components changes.
Old tracer + dev compat (0.0.0) No TCP Not supported

Test coverage

  • Self Monitoring
  • New Unit Tests

@pr-commenter
Copy link
Copy Markdown

pr-commenter Bot commented Feb 6, 2026

Benchmarks

Benchmark execution time: 2026-04-21 20:41:26

Comparing candidate commit b920296 in PR branch lewis/SVLS-8244/add-multifunction-named-pipes with baseline commit e36a35c in branch master.

Found 0 performance improvements and 2 performance regressions! Performance is the same for 25 metrics, 0 unstable metrics, 87 known flaky benchmarks.

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.HttpClientBenchmark.SendAsync net472

  • 🟥 throughput [-5204.038op/s; -4929.460op/s] or [-5.941%; -5.627%]

scenario:Benchmarks.Trace.NLogBenchmark.EnrichedLog net472

  • 🟥 throughput [-8480.430op/s; -7337.877op/s] or [-6.496%; -5.621%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.008%; +0.005%]
  • ignore execution_time [-1.532ms; -0.604ms] or [-0.761%; -0.300%]
  • ignore throughput [+19.868op/s; +527.908op/s] or [+0.024%; +0.626%]

scenario:Benchmarks.Trace.ActivityBenchmark.StartStopWithChild net6.0

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.006%; +0.007%]
  • ignore execution_time [-2.069ms; +6.289ms] or [-1.032%; +3.137%]
  • 🟩 throughput [+8274.344op/s; +11735.462op/s] or [+6.955%; +9.864%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.003%; +0.007%]
  • ignore execution_time [+0.058ms; +4.468ms] or [+0.029%; +2.247%]
  • ignore throughput [-3316.031op/s; -1652.640op/s] or [-3.372%; -1.680%]

scenario:Benchmarks.Trace.AgentWriterBenchmark.WriteAndFlushEnrichedTraces net472

  • ignore allocated_mem [-20 bytes; -19 bytes] or [-0.613%; -0.600%]
  • 🟥 execution_time [+311.981ms; +315.622ms] or [+154.816%; +156.623%]
  • ignore throughput [-5.340op/s; -0.979op/s] or [-0.961%; -0.176%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+0.027%; +0.037%]
  • 🟥 execution_time [+382.495ms; +385.321ms] or [+302.194%; +304.427%]
  • ignore throughput [+11.211op/s; +13.847op/s] or [+1.478%; +1.826%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.009%; +0.002%]
  • 🟥 execution_time [+395.428ms; +399.267ms] or [+349.938%; +353.336%]
  • ignore throughput [-0.970op/s; +2.074op/s] or [-0.137%; +0.293%]

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

  • 🟥 allocated_mem [+1.308KB; +1.308KB] or [+27.529%; +27.541%]
  • ignore execution_time [-174.199µs; +396.693µs] or [-0.087%; +0.198%]
  • ignore throughput [-4781.886op/s; -4398.812op/s] or [-3.721%; -3.423%]

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

  • 🟥 allocated_mem [+471 bytes; +472 bytes] or [+9.977%; +9.987%]
  • 🟩 execution_time [-16.226ms; -12.035ms] or [-7.578%; -5.621%]
  • ignore throughput [+4574.542op/s; +7358.828op/s] or [+3.339%; +5.372%]

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

  • 🟥 allocated_mem [+1.272KB; +1.272KB] or [+27.502%; +27.510%]
  • ignore execution_time [-11.228ms; -7.053ms] or [-5.347%; -3.359%]
  • ignore throughput [-3412.036op/s; -1121.110op/s] or [-3.085%; -1.014%]

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

  • 🟥 allocated_mem [+1.307KB; +1.307KB] or [+105.746%; +105.759%]
  • ignore execution_time [-934.735µs; -392.614µs] or [-0.465%; -0.195%]
  • 🟥 throughput [-272468.999op/s; -269519.243op/s] or [-27.820%; -27.519%]

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

  • 🟥 allocated_mem [+471 bytes; +472 bytes] or [+38.558%; +38.566%]
  • 🟩 execution_time [-26.145ms; -21.273ms] or [-11.660%; -9.487%]
  • ignore throughput [-63472.102op/s; -40778.231op/s] or [-6.781%; -4.356%]

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

  • 🟥 allocated_mem [+1.272KB; +1.272KB] or [+105.292%; +105.304%]
  • ignore execution_time [-0.782ms; +3.498ms] or [-0.390%; +1.746%]
  • 🟥 throughput [-138339.319op/s; -122466.729op/s] or [-19.877%; -17.596%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.007%; +0.003%]
  • ignore execution_time [-1060.853µs; +0.870µs] or [-0.529%; +0.000%]
  • ignore throughput [-457.440op/s; +362.473op/s] or [-0.308%; +0.244%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.004%; +0.003%]
  • ignore execution_time [+0.512ms; +4.016ms] or [+0.258%; +2.026%]
  • 🟩 throughput [+10247.072op/s; +13181.817op/s] or [+6.520%; +8.387%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.007%; +0.003%]
  • ignore execution_time [+0.860ms; +4.913ms] or [+0.438%; +2.505%]
  • 🟩 throughput [+6626.969op/s; +9317.819op/s] or [+5.279%; +7.423%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.008%; +0.004%]
  • ignore execution_time [-440.631µs; -190.882µs] or [-0.220%; -0.095%]
  • ignore throughput [+37940.595op/s; +83048.134op/s] or [+1.154%; +2.526%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.006%; +0.008%]
  • ignore execution_time [-1.991ms; -1.010ms] or [-0.984%; -0.500%]
  • 🟩 throughput [+500655.655op/s; +517678.463op/s] or [+16.694%; +17.262%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.004%; +0.004%]
  • 🟩 execution_time [-18.324ms; -13.980ms] or [-8.447%; -6.444%]
  • 🟩 throughput [+189721.538op/s; +243586.060op/s] or [+7.531%; +9.669%]

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

  • ignore allocated_mem [+0 bytes; +2 bytes] or [-0.001%; +0.007%]
  • 🟥 execution_time [+299.898ms; +300.421ms] or [+149.849%; +150.110%]
  • ignore throughput [+161.697op/s; +177.256op/s] or [+1.786%; +1.958%]

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

  • ignore allocated_mem [-1 bytes; +2 bytes] or [-0.004%; +0.008%]
  • 🟥 execution_time [+299.695ms; +302.967ms] or [+151.137%; +152.787%]
  • ignore throughput [+314.105op/s; +527.190op/s] or [+2.402%; +4.032%]

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

  • ignore allocated_mem [-1 bytes; +2 bytes] or [-0.004%; +0.008%]
  • 🟥 execution_time [+300.521ms; +302.988ms] or [+151.379%; +152.622%]
  • ignore throughput [+125.414op/s; +254.506op/s] or [+1.211%; +2.457%]

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

  • ignore allocated_mem [+3 bytes; +4 bytes] or [+0.186%; +0.199%]
  • 🟥 execution_time [+297.224ms; +298.079ms] or [+145.985%; +146.405%]
  • ignore throughput [+10.980op/s; +16.293op/s] or [+0.291%; +0.432%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.004%; +0.009%]
  • 🟥 execution_time [+296.152ms; +298.420ms] or [+144.778%; +145.887%]
  • ignore throughput [+166.897op/s; +205.029op/s] or [+2.425%; +2.979%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.004%; +0.009%]
  • 🟥 execution_time [+301.566ms; +302.857ms] or [+150.722%; +151.368%]
  • ignore throughput [+23.472op/s; +43.206op/s] or [+0.466%; +0.858%]

scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+nan%; +nan%]
  • ignore execution_time [+6.881µs; +11.779µs] or [+1.413%; +2.418%]
  • ignore throughput [-47.616op/s; -28.072op/s] or [-2.319%; -1.367%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.000%; +0.010%]
  • 🟥 execution_time [+23.685µs; +50.532µs] or [+5.432%; +11.590%]
  • 🟥 throughput [-245.865op/s; -125.126op/s] or [-10.689%; -5.440%]

scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmark netcoreapp3.1

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.003%; +0.003%]
  • ignore execution_time [+10.308µs; +32.302µs] or [+2.209%; +6.921%]
  • ignore throughput [-157.451op/s; -76.550op/s] or [-7.268%; -3.534%]

scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+nan%; +nan%]
  • ignore execution_time [-3.825µs; +0.341µs] or [-1.033%; +0.092%]
  • ignore throughput [-1.606op/s; +28.337op/s] or [-0.059%; +1.049%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.003%; +0.007%]
  • 🟥 execution_time [+24.260µs; +47.929µs] or [+7.745%; +15.301%]
  • 🟥 throughput [-443.751op/s; -244.676op/s] or [-13.833%; -7.627%]

scenario:Benchmarks.Trace.Asm.AppSecWafBenchmark.RunWafRealisticBenchmarkWithAttack netcoreapp3.1

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.003%; +0.003%]
  • ignore execution_time [-13.293µs; +9.091µs] or [-3.636%; +2.487%]
  • ignore throughput [-101.522op/s; +32.079op/s] or [-3.643%; +1.151%]

scenario:Benchmarks.Trace.AspNetCoreBenchmark.SendRequest net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+nan%; +nan%]
  • 🟥 execution_time [+299.536ms; +300.219ms] or [+149.499%; +149.840%]
  • ignore throughput [+3630340.336op/s; +4196307.002op/s] or [+1.818%; +2.102%]

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

  • ignore allocated_mem [+88 bytes; +90 bytes] or [+0.491%; +0.502%]
  • 🟥 execution_time [+409.346ms; +413.868ms] or [+444.771%; +449.685%]
  • 🟩 throughput [+1098.290op/s; +1250.754op/s] or [+9.025%; +10.278%]

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

  • ignore allocated_mem [+20 bytes; +22 bytes] or [+0.099%; +0.110%]
  • unstable execution_time [+331.555ms; +360.851ms] or [+251.746%; +273.991%]
  • 🟩 throughput [+700.681op/s; +904.296op/s] or [+6.783%; +8.754%]

scenario:Benchmarks.Trace.CIVisibilityProtocolWriterBenchmark.WriteAndFlushEnrichedTraces net472

  • 🟥 allocated_mem [+2.859KB; +2.864KB] or [+5.080%; +5.088%]
  • unstable execution_time [+262.588ms; +349.766ms] or [+120.735%; +160.819%]
  • 🟥 throughput [-492.149op/s; -432.215op/s] or [-44.594%; -39.163%]

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

  • ignore allocated_mem [-1.270KB; -1.268KB] or [-2.995%; -2.990%]
  • unstable execution_time [+204.080ms; +337.288ms] or [+86.970%; +143.738%]
  • 🟥 throughput [-746.238op/s; -662.813op/s] or [-49.774%; -44.210%]

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

  • ignore allocated_mem [+986 bytes; +989 bytes] or [+2.329%; +2.336%]
  • 🟥 execution_time [+332.762ms; +340.253ms] or [+199.030%; +203.511%]
  • 🟥 throughput [-398.457op/s; -362.869op/s] or [-27.744%; -25.266%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+nan%; +nan%]
  • ignore execution_time [-97.614µs; -78.157µs] or [-4.912%; -3.933%]
  • ignore throughput [+20.970op/s; +26.279op/s] or [+4.167%; +5.222%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+nan%; +nan%]
  • ignore execution_time [-38.006µs; -25.881µs] or [-2.611%; -1.778%]
  • ignore throughput [+12.713op/s; +18.651op/s] or [+1.851%; +2.715%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSlice netcoreapp3.1

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+nan%; +nan%]
  • ignore execution_time [-196.385µs; -105.155µs] or [-6.832%; -3.658%]
  • ignore throughput [+13.778op/s; +32.976op/s] or [+3.960%; +9.479%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+nan%; +nan%]
  • ignore execution_time [-7.429µs; +2.551µs] or [-0.642%; +0.220%]
  • ignore throughput [-1.492op/s; +5.869op/s] or [-0.173%; +0.680%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+nan%; +nan%]
  • ignore execution_time [-27.997µs; -15.305µs] or [-2.597%; -1.419%]
  • ignore throughput [+14.013op/s; +25.539op/s] or [+1.511%; +2.754%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OptimizedCharSliceWithPool netcoreapp3.1

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+nan%; +nan%]
  • ignore execution_time [-87.277µs; -2.754µs] or [-4.676%; -0.148%]
  • unstable throughput [+2.072op/s; +56.694op/s] or [+0.387%; +10.582%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice net472

  • ignore allocated_mem [-43 bytes; +21 bytes] or [-0.007%; +0.003%]
  • ignore execution_time [-7.335µs; +1.674µs] or [-0.287%; +0.065%]
  • ignore throughput [-0.226op/s; +1.145op/s] or [-0.058%; +0.293%]

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

  • ignore allocated_mem [-38 bytes; +46 bytes] or [-0.006%; +0.007%]
  • 🟩 execution_time [-161.324µs; -118.609µs] or [-8.172%; -6.008%]
  • 🟩 throughput [+34.844op/s; +45.716op/s] or [+6.878%; +9.025%]

scenario:Benchmarks.Trace.CharSliceBenchmark.OriginalCharSlice netcoreapp3.1

  • ignore allocated_mem [-42 bytes; +23 bytes] or [-0.007%; +0.004%]
  • ignore execution_time [-243.560µs; -86.322µs] or [-6.176%; -2.189%]
  • ignore throughput [+6.407op/s; +26.187op/s] or [+2.526%; +10.326%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearch net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.001%; +0.008%]
  • 🟥 execution_time [+303.627ms; +305.195ms] or [+152.901%; +153.691%]
  • ignore throughput [+8520.054op/s; +10394.327op/s] or [+2.742%; +3.345%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.006%; +0.004%]
  • 🟥 execution_time [+300.800ms; +302.601ms] or [+150.731%; +151.634%]
  • ignore throughput [+19836.873op/s; +25321.605op/s] or [+3.127%; +3.992%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.006%; +0.004%]
  • 🟥 execution_time [+299.546ms; +302.871ms] or [+150.479%; +152.150%]
  • ignore throughput [+20023.697op/s; +27572.898op/s] or [+4.218%; +5.808%]

scenario:Benchmarks.Trace.ElasticsearchBenchmark.CallElasticsearchAsync net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.008%; +0.004%]
  • 🟥 execution_time [+301.969ms; +303.489ms] or [+151.639%; +152.402%]
  • ignore throughput [-1039.430op/s; +1120.834op/s] or [-0.348%; +0.375%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.007%; +0.006%]
  • 🟥 execution_time [+297.680ms; +299.508ms] or [+147.189%; +148.093%]
  • ignore throughput [+12454.318op/s; +16469.501op/s] or [+2.007%; +2.654%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.006%; +0.004%]
  • 🟥 execution_time [+303.859ms; +307.255ms] or [+154.009%; +155.730%]
  • ignore throughput [+10505.207op/s; +18538.682op/s] or [+2.269%; +4.003%]

scenario:Benchmarks.Trace.GraphQLBenchmark.ExecuteAsync net472

  • ignore allocated_mem [+0 bytes; +1 bytes] or [+0.108%; +0.119%]
  • 🟥 execution_time [+300.911ms; +303.607ms] or [+151.030%; +152.383%]
  • ignore throughput [+3006.946op/s; +6079.714op/s] or [+0.780%; +1.577%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.006%; +0.006%]
  • 🟥 execution_time [+299.677ms; +301.583ms] or [+149.361%; +150.311%]
  • 🟩 throughput [+47993.867op/s; +52604.319op/s] or [+9.530%; +10.446%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.006%; +0.006%]
  • 🟥 execution_time [+300.867ms; +303.637ms] or [+149.679%; +151.057%]
  • ignore throughput [-8737.351op/s; -3549.570op/s] or [-2.068%; -0.840%]

scenario:Benchmarks.Trace.ILoggerBenchmark.EnrichedLog net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.003%; +0.006%]
  • ignore execution_time [-2.163ms; -1.184ms] or [-1.076%; -0.589%]
  • ignore throughput [+5010.549op/s; +6413.932op/s] or [+2.015%; +2.579%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.005%; +0.003%]
  • 🟩 execution_time [-16.156ms; -11.922ms] or [-7.512%; -5.544%]
  • 🟩 throughput [+25847.632op/s; +33138.861op/s] or [+7.091%; +9.091%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.004%; +0.008%]
  • ignore execution_time [-0.428ms; +3.525ms] or [-0.215%; +1.768%]
  • ignore throughput [-2423.287op/s; +3788.632op/s] or [-0.885%; +1.383%]

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

  • ignore allocated_mem [+3.735KB; +3.763KB] or [+1.359%; +1.369%]
  • unstable execution_time [+6.697µs; +47.528µs] or [+1.654%; +11.740%]
  • ignore throughput [-247.294op/s; -41.881op/s] or [-9.951%; -1.685%]

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

  • 🟩 allocated_mem [-20.960KB; -20.940KB] or [-7.646%; -7.638%]
  • unstable execution_time [-46.816µs; +5.581µs] or [-9.253%; +1.103%]
  • ignore throughput [-9.768op/s; +173.211op/s] or [-0.487%; +8.643%]

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

  • ignore allocated_mem [-1.013KB; -0.996KB] or [-0.369%; -0.363%]
  • ignore execution_time [-45.690µs; +11.163µs] or [-7.918%; +1.934%]
  • ignore throughput [-21.364op/s; +133.608op/s] or [-1.221%; +7.633%]

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

  • ignore allocated_mem [-2 bytes; +2 bytes] or [-0.005%; +0.006%]
  • ignore execution_time [+0.839µs; +2.260µs] or [+1.453%; +3.915%]
  • ignore throughput [-642.823op/s; -243.626op/s] or [-3.709%; -1.406%]

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

  • ignore allocated_mem [-4 bytes; +0 bytes] or [-0.010%; -0.001%]
  • unstable execution_time [+8.002µs; +12.536µs] or [+18.915%; +29.632%]
  • 🟥 throughput [-5340.092op/s; -3499.446op/s] or [-22.480%; -14.732%]

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

  • ignore allocated_mem [-1 bytes; +1 bytes] or [-0.002%; +0.002%]
  • unstable execution_time [-11.798µs; -4.254µs] or [-18.304%; -6.600%]
  • 🟩 throughput [+1035.817op/s; +2600.399op/s] or [+6.355%; +15.954%]

scenario:Benchmarks.Trace.Log4netBenchmark.EnrichedLog net472

  • ignore allocated_mem [+1 bytes; +2 bytes] or [+0.039%; +0.050%]
  • 🟥 execution_time [+302.522ms; +305.074ms] or [+152.912%; +154.201%]
  • ignore throughput [-134.392op/s; -101.093op/s] or [-2.246%; -1.689%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.005%; +0.005%]
  • 🟥 execution_time [+303.289ms; +305.690ms] or [+154.373%; +155.595%]
  • ignore throughput [-116.304op/s; -40.515op/s] or [-1.442%; -0.502%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.005%; +0.005%]
  • 🟥 execution_time [+298.985ms; +301.123ms] or [+149.679%; +150.749%]
  • ignore throughput [-147.386op/s; -83.380op/s] or [-1.877%; -1.062%]

scenario:Benchmarks.Trace.RedisBenchmark.SendReceive net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.005%; +0.005%]
  • ignore execution_time [-100.642µs; +378.053µs] or [-0.050%; +0.188%]
  • ignore throughput [-3949.635op/s; -2744.000op/s] or [-1.093%; -0.760%]

scenario:Benchmarks.Trace.RedisBenchmark.SendReceive net6.0

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.004%; +0.007%]
  • ignore execution_time [-117.529µs; +627.587µs] or [-0.059%; +0.314%]
  • 🟩 throughput [+40137.166op/s; +43705.768op/s] or [+7.597%; +8.273%]

scenario:Benchmarks.Trace.RedisBenchmark.SendReceive netcoreapp3.1

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.004%; +0.006%]
  • ignore execution_time [+1.123ms; +4.849ms] or [+0.569%; +2.458%]
  • ignore throughput [-3259.904op/s; +5235.363op/s] or [-0.772%; +1.239%]

scenario:Benchmarks.Trace.SerilogBenchmark.EnrichedLog net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.005%; +0.006%]
  • 🟥 execution_time [+300.269ms; +302.025ms] or [+149.657%; +150.532%]
  • ignore throughput [-4618.742op/s; -3628.111op/s] or [-3.050%; -2.396%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+0.000%; +0.009%]
  • 🟥 execution_time [+302.670ms; +304.148ms] or [+151.987%; +152.729%]
  • ignore throughput [+2276.854op/s; +3741.945op/s] or [+0.990%; +1.627%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.004%; +0.003%]
  • 🟥 execution_time [+302.739ms; +304.882ms] or [+153.530%; +154.617%]
  • ignore throughput [+1564.628op/s; +3589.806op/s] or [+0.881%; +2.022%]

scenario:Benchmarks.Trace.SingleSpanAspNetCoreBenchmark.SingleSpanAspNetCore net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+nan%; +nan%]
  • 🟥 execution_time [+299.952ms; +300.868ms] or [+149.618%; +150.074%]
  • 🟩 throughput [+66094140.314op/s; +66380186.405op/s] or [+48.134%; +48.342%]

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

  • ignore allocated_mem [+69 bytes; +71 bytes] or [+0.406%; +0.416%]
  • unstable execution_time [+341.916ms; +384.358ms] or [+425.233%; +478.018%]
  • 🟩 throughput [+902.456op/s; +1090.022op/s] or [+6.976%; +8.426%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [+nan%; +nan%]
  • 🟥 execution_time [+299.590ms; +300.518ms] or [+149.429%; +149.892%]
  • ignore throughput [+1689772.192op/s; +2632286.600op/s] or [+0.748%; +1.166%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishScope net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.005%; +0.006%]
  • ignore execution_time [+561.268µs; +1099.360µs] or [+0.281%; +0.550%]
  • ignore throughput [-14006.448op/s; -9855.768op/s] or [-1.563%; -1.100%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.005%; +0.007%]
  • ignore execution_time [-4.722ms; -3.608ms] or [-2.313%; -1.767%]
  • 🟩 throughput [+108357.089op/s; +118381.386op/s] or [+10.117%; +11.053%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.003%; +0.006%]
  • ignore execution_time [-0.121ms; +3.988ms] or [-0.061%; +2.018%]
  • 🟩 throughput [+52599.519op/s; +72503.103op/s] or [+6.088%; +8.392%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.000%; +0.009%]
  • ignore execution_time [-86.865µs; +281.074µs] or [-0.043%; +0.140%]
  • ignore throughput [-13195.014op/s; -7914.740op/s] or [-1.208%; -0.725%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.004%; +0.004%]
  • ignore execution_time [+6.822ms; +10.938ms] or [+3.554%; +5.699%]
  • 🟩 throughput [+74019.870op/s; +106353.504op/s] or [+5.729%; +8.232%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.001%; +0.008%]
  • ignore execution_time [-4.212ms; -2.764ms] or [-2.070%; -1.358%]
  • 🟩 throughput [+80105.267op/s; +88757.254op/s] or [+7.956%; +8.815%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.008%; +0.002%]
  • ignore execution_time [-1487.102µs; -213.234µs] or [-0.740%; -0.106%]
  • ignore throughput [+15286.104op/s; +18391.476op/s] or [+3.406%; +4.098%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.004%; +0.009%]
  • ignore execution_time [+98.812µs; +1600.777µs] or [+0.049%; +0.799%]
  • 🟩 throughput [+50838.318op/s; +55271.656op/s] or [+9.231%; +10.036%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.006%; +0.004%]
  • ignore execution_time [-1830.559µs; +2255.906µs] or [-0.920%; +1.133%]
  • 🟩 throughput [+25808.296op/s; +36250.762op/s] or [+5.777%; +8.114%]

scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin net472

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.005%; +0.006%]
  • ignore execution_time [-1190.392µs; -205.761µs] or [-0.593%; -0.103%]
  • 🟥 throughput [-39744.577op/s; -35936.395op/s] or [-5.817%; -5.260%]

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

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.005%; +0.007%]
  • ignore execution_time [-0.421ms; +3.115ms] or [-0.211%; +1.558%]
  • ignore throughput [+38617.576op/s; +57181.427op/s] or [+4.315%; +6.389%]

scenario:Benchmarks.Trace.TraceAnnotationsBenchmark.RunOnMethodBegin netcoreapp3.1

  • ignore allocated_mem [+0 bytes; +0 bytes] or [-0.005%; +0.005%]
  • ignore execution_time [+1.720ms; +5.721ms] or [+0.873%; +2.905%]
  • ignore throughput [+23979.234op/s; +39125.655op/s] or [+3.348%; +5.463%]

@dd-trace-dotnet-ci-bot
Copy link
Copy Markdown

dd-trace-dotnet-ci-bot Bot commented Feb 6, 2026

Execution-Time Benchmarks Report ⏱️

Execution-time results for samples comparing This PR (8164) 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
duration72.79 ± (72.96 - 73.46) ms72.37 ± (72.48 - 72.85) ms-0.6%
.NET Framework 4.8 - Bailout
duration76.75 ± (76.64 - 77.01) ms76.50 ± (76.37 - 76.71) ms-0.3%
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1072.91 ± (1072.04 - 1078.41) ms1083.95 ± (1083.20 - 1089.75) ms+1.0%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms22.65 ± (22.59 - 22.71) ms22.38 ± (22.34 - 22.41) ms-1.2%
process.time_to_main_ms85.92 ± (85.59 - 86.26) ms83.47 ± (83.28 - 83.67) ms-2.9%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.85 ± (10.84 - 10.85) MB10.90 ± (10.90 - 10.91) MB+0.5%✅⬆️
runtime.dotnet.threads.count12 ± (12 - 12)12 ± (12 - 12)+0.0%
.NET Core 3.1 - Bailout
process.internal_duration_ms22.42 ± (22.38 - 22.46) ms22.73 ± (22.66 - 22.79) ms+1.4%✅⬆️
process.time_to_main_ms85.54 ± (85.33 - 85.75) ms87.05 ± (86.69 - 87.41) ms+1.8%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.94 ± (10.94 - 10.94) MB10.93 ± (10.93 - 10.94) MB-0.1%
runtime.dotnet.threads.count13 ± (13 - 13)13 ± (13 - 13)+0.0%
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms230.89 ± (229.68 - 232.10) ms230.40 ± (229.25 - 231.54) ms-0.2%
process.time_to_main_ms528.35 ± (526.80 - 529.89) ms531.61 ± (530.08 - 533.14) ms+0.6%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed48.54 ± (48.51 - 48.56) MB48.42 ± (48.39 - 48.45) MB-0.2%
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)+0.0%
.NET 6 - Baseline
process.internal_duration_ms21.22 ± (21.17 - 21.27) ms21.32 ± (21.27 - 21.38) ms+0.5%✅⬆️
process.time_to_main_ms73.72 ± (73.46 - 73.98) ms74.03 ± (73.74 - 74.32) ms+0.4%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.63 ± (10.63 - 10.63) MB10.63 ± (10.63 - 10.63) MB+0.0%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 6 - Bailout
process.internal_duration_ms21.21 ± (21.16 - 21.25) ms21.07 ± (21.03 - 21.11) ms-0.7%
process.time_to_main_ms75.00 ± (74.78 - 75.23) ms74.10 ± (73.93 - 74.27) ms-1.2%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.74 ± (10.73 - 10.74) MB10.75 ± (10.75 - 10.75) MB+0.1%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms383.31 ± (381.47 - 385.14) ms382.25 ± (380.19 - 384.30) ms-0.3%
process.time_to_main_ms528.77 ± (527.63 - 529.91) ms532.76 ± (531.39 - 534.13) ms+0.8%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed49.88 ± (49.85 - 49.90) MB50.10 ± (50.07 - 50.12) MB+0.4%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)-0.1%
.NET 8 - Baseline
process.internal_duration_ms19.42 ± (19.38 - 19.45) ms19.92 ± (19.86 - 19.99) ms+2.6%✅⬆️
process.time_to_main_ms72.54 ± (72.38 - 72.71) ms75.93 ± (75.62 - 76.25) ms+4.7%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.67 ± (7.67 - 7.68) MB7.67 ± (7.67 - 7.68) MB+0.0%✅⬆️
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 8 - Bailout
process.internal_duration_ms19.63 ± (19.58 - 19.69) ms19.66 ± (19.62 - 19.71) ms+0.2%✅⬆️
process.time_to_main_ms75.08 ± (74.83 - 75.34) ms75.50 ± (75.29 - 75.72) ms+0.6%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.72 ± (7.72 - 7.72) MB7.72 ± (7.72 - 7.73) MB+0.0%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms305.39 ± (303.09 - 307.68) ms305.41 ± (303.11 - 307.71) ms+0.0%✅⬆️
process.time_to_main_ms489.92 ± (488.77 - 491.07) ms492.66 ± (491.38 - 493.95) ms+0.6%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed37.20 ± (37.16 - 37.23) MB37.07 ± (37.03 - 37.11) MB-0.3%
runtime.dotnet.threads.count27 ± (27 - 27)27 ± (27 - 27)-0.4%

HttpMessageHandler

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration190.88 ± (190.94 - 191.70) ms191.89 ± (191.67 - 192.26) ms+0.5%✅⬆️
.NET Framework 4.8 - Bailout
duration194.62 ± (194.58 - 194.93) ms196.32 ± (196.07 - 196.62) ms+0.9%✅⬆️
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1147.54 ± (1149.71 - 1156.69) ms1160.03 ± (1160.20 - 1167.14) ms+1.1%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms185.07 ± (184.83 - 185.31) ms189.13 ± (188.69 - 189.57) ms+2.2%✅⬆️
process.time_to_main_ms80.72 ± (80.50 - 80.93) ms81.97 ± (81.73 - 82.21) ms+1.5%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.12 ± (16.10 - 16.14) MB16.06 ± (16.03 - 16.09) MB-0.4%
runtime.dotnet.threads.count20 ± (19 - 20)20 ± (19 - 20)-0.0%
.NET Core 3.1 - Bailout
process.internal_duration_ms184.54 ± (184.29 - 184.78) ms186.82 ± (186.52 - 187.12) ms+1.2%✅⬆️
process.time_to_main_ms81.73 ± (81.59 - 81.87) ms83.02 ± (82.84 - 83.20) ms+1.6%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.18 ± (16.15 - 16.21) MB16.15 ± (16.11 - 16.18) MB-0.2%
runtime.dotnet.threads.count21 ± (21 - 21)21 ± (21 - 21)+0.4%✅⬆️
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms393.76 ± (392.43 - 395.09) ms397.82 ± (396.39 - 399.24) ms+1.0%✅⬆️
process.time_to_main_ms506.15 ± (504.88 - 507.42) ms514.16 ± (512.84 - 515.48) ms+1.6%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed58.74 ± (58.54 - 58.94) MB59.17 ± (59.03 - 59.30) MB+0.7%✅⬆️
runtime.dotnet.threads.count30 ± (30 - 30)30 ± (29 - 30)-0.1%
.NET 6 - Baseline
process.internal_duration_ms189.30 ± (188.99 - 189.62) ms196.19 ± (195.61 - 196.77) ms+3.6%✅⬆️
process.time_to_main_ms70.10 ± (69.95 - 70.26) ms72.38 ± (72.17 - 72.59) ms+3.2%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.06 ± (15.91 - 16.21) MB16.36 ± (16.34 - 16.38) MB+1.9%✅⬆️
runtime.dotnet.threads.count18 ± (18 - 18)19 ± (19 - 19)+5.7%✅⬆️
.NET 6 - Bailout
process.internal_duration_ms188.63 ± (188.41 - 188.86) ms191.13 ± (190.92 - 191.34) ms+1.3%✅⬆️
process.time_to_main_ms71.02 ± (70.93 - 71.11) ms71.95 ± (71.83 - 72.08) ms+1.3%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.04 ± (15.88 - 16.21) MB16.34 ± (16.25 - 16.43) MB+1.9%✅⬆️
runtime.dotnet.threads.count19 ± (19 - 19)20 ± (19 - 20)+3.2%✅⬆️
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms600.13 ± (597.64 - 602.61) ms601.30 ± (599.06 - 603.53) ms+0.2%✅⬆️
process.time_to_main_ms508.40 ± (507.52 - 509.29) ms519.27 ± (518.15 - 520.38) ms+2.1%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed61.71 ± (61.62 - 61.80) MB61.88 ± (61.79 - 61.96) MB+0.3%✅⬆️
runtime.dotnet.threads.count30 ± (30 - 30)30 ± (30 - 30)+0.1%✅⬆️
.NET 8 - Baseline
process.internal_duration_ms186.80 ± (186.51 - 187.09) ms191.83 ± (191.49 - 192.18) ms+2.7%✅⬆️
process.time_to_main_ms69.39 ± (69.23 - 69.55) ms71.46 ± (71.24 - 71.68) ms+3.0%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.77 ± (11.74 - 11.80) MB11.74 ± (11.72 - 11.76) MB-0.3%
runtime.dotnet.threads.count18 ± (18 - 18)18 ± (18 - 18)+0.1%✅⬆️
.NET 8 - Bailout
process.internal_duration_ms185.76 ± (185.61 - 185.91) ms190.91 ± (190.61 - 191.22) ms+2.8%✅⬆️
process.time_to_main_ms70.46 ± (70.41 - 70.52) ms72.60 ± (72.48 - 72.72) ms+3.0%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.70 ± (11.62 - 11.78) MB11.82 ± (11.79 - 11.84) MB+1.0%✅⬆️
runtime.dotnet.threads.count19 ± (18 - 19)19 ± (19 - 19)+3.3%✅⬆️
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms518.13 ± (515.84 - 520.42) ms521.75 ± (519.00 - 524.50) ms+0.7%✅⬆️
process.time_to_main_ms466.87 ± (466.22 - 467.53) ms477.25 ± (476.37 - 478.12) ms+2.2%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed50.75 ± (50.72 - 50.78) MB50.80 ± (50.77 - 50.83) MB+0.1%✅⬆️
runtime.dotnet.threads.count30 ± (30 - 30)30 ± (30 - 30)+0.1%✅⬆️
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 (8164) - mean (73ms)  : 70, 75
    master - mean (73ms)  : 70, 77

    section Bailout
    This PR (8164) - mean (77ms)  : 75, 78
    master - mean (77ms)  : 75, 79

    section CallTarget+Inlining+NGEN
    This PR (8164) - mean (1,086ms)  : 1039, 1134
    master - mean (1,075ms)  : 1030, 1121

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 (8164) - mean (113ms)  : 108, 118
    master - mean (116ms)  : 108, 123

    section Bailout
    This PR (8164) - mean (117ms)  : 109, 125
    master - mean (115ms)  : 111, 118

    section CallTarget+Inlining+NGEN
    This PR (8164) - mean (800ms)  : 770, 829
    master - mean (797ms)  : 773, 821

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

    section Bailout
    This PR (8164) - mean (101ms)  : 99, 104
    master - mean (103ms)  : 98, 108

    section CallTarget+Inlining+NGEN
    This PR (8164) - mean (945ms)  : 910, 980
    master - mean (940ms)  : 906, 974

Loading
FakeDbCommand (.NET 8)
gantt
    title Execution time (ms) FakeDbCommand (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8164) - mean (104ms)  : 99, 109
    master - mean (100ms)  : 96, 103

    section Bailout
    This PR (8164) - mean (104ms)  : 98, 109
    master - mean (103ms)  : 97, 108

    section CallTarget+Inlining+NGEN
    This PR (8164) - mean (831ms)  : 775, 888
    master - mean (825ms)  : 786, 865

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 (8164) - mean (192ms)  : 189, 195
    master - mean (191ms)  : 188, 195

    section Bailout
    This PR (8164) - mean (196ms)  : 194, 199
    master - mean (195ms)  : 193, 196

    section CallTarget+Inlining+NGEN
    This PR (8164) - mean (1,164ms)  : 1114, 1214
    master - mean (1,153ms)  : 1101, 1205

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 (8164) - mean (281ms)  : 272, 289
    master - mean (274ms)  : 270, 278

    section Bailout
    This PR (8164) - mean (278ms)  : 274, 283
    master - mean (275ms)  : 271, 279

    section CallTarget+Inlining+NGEN
    This PR (8164) - mean (948ms)  : 926, 970
    master - mean (930ms)  : 903, 957

Loading
HttpMessageHandler (.NET 6)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8164) - mean (277ms)  : 265, 289
    master - mean (267ms)  : 263, 272

    section Bailout
    This PR (8164) - mean (271ms)  : 269, 274
    master - mean (268ms)  : 265, 271

    section CallTarget+Inlining+NGEN
    This PR (8164) - mean (1,150ms)  : 1116, 1185
    master - mean (1,138ms)  : 1099, 1176

Loading
HttpMessageHandler (.NET 8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8164) - mean (274ms)  : 269, 279
    master - mean (266ms)  : 261, 271

    section Bailout
    This PR (8164) - mean (274ms)  : 270, 278
    master - mean (266ms)  : 263, 268

    section CallTarget+Inlining+NGEN
    This PR (8164) - mean (1,033ms)  : 989, 1076
    master - mean (1,019ms)  : 981, 1057

Loading

@Lewis-E Lewis-E force-pushed the lewis/SVLS-8244/add-multifunction-named-pipes branch 2 times, most recently from aaff9b5 to 08a6c72 Compare February 12, 2026 15:09
@Lewis-E Lewis-E force-pushed the lewis/SVLS-8244/add-multifunction-named-pipes branch 3 times, most recently from 2fcae54 to edb4121 Compare March 6, 2026 18:23
@Lewis-E Lewis-E marked this pull request as ready for review March 6, 2026 18:57
@Lewis-E Lewis-E requested review from a team as code owners March 6, 2026 18:57
@Lewis-E Lewis-E requested a review from duncanpharvey March 6, 2026 18:57
Copy link
Copy Markdown
Contributor

@duncanpharvey duncanpharvey left a comment

Choose a reason for hiding this comment

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

We check the presence of the compat layer + windows configs (using undocumented envvar DD_SERVERLESS_COMPAT_BINARY_PATH if necessary to adapt to Azure changes) to see if named pipes can be used or if we should rely on TCP

What is meant by this? I don't see DD_SERVERLESS_COMPAT_BINARY_PATH in any of the PR changes.

Comment thread tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs Outdated
@duncanpharvey
Copy link
Copy Markdown
Contributor

Should this dependency for Datadog.Serverless.Compat also be updated as part of this PR?

<PackageReference Include="Datadog.Serverless.Compat" Version="1.2.0" PrivateAssets="none" />

@lucaspimentel
Copy link
Copy Markdown
Member

@codex review

Comment thread tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs Outdated
&& !Util.EnvironmentHelpers.IsUsingAzureAppServicesSiteExtension()
&& IsCompatLayerAvailableWithPipeSupport())
{
var baseName = Util.EnvironmentHelpers.GetEnvironmentVariable(ConfigurationKeys.TracesPipeName);
Copy link
Copy Markdown
Member

@lucaspimentel lucaspimentel Mar 23, 2026

Choose a reason for hiding this comment

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

This is only reading the "base" name from environment variables, but the tracer can be configured in other ways (e.g. config files), and those configs would be ignored there. Instead of reading from env vars directly, I think you can read the user setting from rawSettings.TracesPipeName, passed in via parameter.

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.

Agreed, but we also need to make sure we record the new "calculated" value in telemetry as well

Copy link
Copy Markdown
Member

@lucaspimentel lucaspimentel Mar 24, 2026

Choose a reason for hiding this comment

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

Looks like my comment is addressed (no reading env vars directly), but I'll leave this open for Andrew's comment (configuration telemetry).

Comment thread tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs Outdated
Comment thread tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs Outdated
Comment thread tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs Outdated
Comment thread tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs Outdated
Copy link
Copy Markdown
Member

@andrewlock andrewlock left a comment

Choose a reason for hiding this comment

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

LGTM in general, but there's some specific things to address before we merge it (mostly already highlighted by Lucas)

  • We should only target existing major versions, and bump support later, otherwise we need strict backward and forward compatibility, which is very hard to maintain and very easy to screw up 😅
  • We shouldn't have static state if we can help it. If we want to cache things, we typically handle that by:
    • Move the calculating and storage of the value to a helper type as instance methods.
    • Create a "singleton" instance of the helper
    • Pass the singleton in to the constructor (or, if this gives too much blast radius, allow passing in a null value for the helper, and grab the singleton in the body. This isn't the preferred approach, but it's sometimes the practical one)
  • We need to ensure we record the new calculated values in telemetry
  • The instrumentation seems a little overly complex considering the behaviours we need and the failure cases

Comment thread tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs Outdated
Comment thread tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs Outdated
&& !Util.EnvironmentHelpers.IsUsingAzureAppServicesSiteExtension()
&& IsCompatLayerAvailableWithPipeSupport())
{
var baseName = Util.EnvironmentHelpers.GetEnvironmentVariable(ConfigurationKeys.TracesPipeName);
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.

Agreed, but we also need to make sure we record the new "calculated" value in telemetry as well

Comment thread tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs Outdated
Comment thread tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs Outdated
@Lewis-E Lewis-E force-pushed the lewis/SVLS-8244/add-multifunction-named-pipes branch from c9b7aee to 1df4f5a Compare March 24, 2026 18:55
Comment thread tracer/src/Datadog.Trace/Configuration/ExporterSettings.cs Outdated
Lewis-E and others added 25 commits April 21, 2026 13:17
Lower MinimumVersion from 1.0.0 to 0.0.0 for the CalculateTracePipeName and
CalculateDogStatsDPipeName integrations so the dev (0.0.0) compat assembly is
also instrumented. Add missing trimming XML entries for types newly referenced
by the tracer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…on expressions

- Remove duplicated GenerateUniquePipeName from ExporterSettings, delegate to
  ServerlessCompatPipeNameHelper
- Remove #if !NETFRAMEWORK guard from helper (no .NET Core-only APIs used)
- Remove redundant Log.Information from helper (callers already log)
- Use [] instead of new string[0] for ParameterTypeNames per project convention

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ions

- ServerlessCompatPipeNameHelper: format, truncation, uniqueness
- Integration OnMethodEnd: exception passthrough, caching, override behavior
- ExporterSettings: constructor pipe name selection, generated name format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Check for the compat binary and DLL on disk before generating pipe names
in ExporterSettings static init. This prevents the tracer from switching
to pipe transport when no compat layer is deployed to listen on the pipe.

The version check allows 0.0.0 (dev builds) or >= 1.4.0 (first release
with named pipe support). Also fix integration fallbacks to pass through
the compat layer's return value instead of generating a new random GUID
when no pre-generated pipe name exists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wrap RuntimeInformation.IsOSPlatform in #if !NETFRAMEWORK (net461 doesn't have it)
- Add null-coalescing for AppDomain.CurrentDomain.BaseDirectory (nullable warning)
- Remove unused argument from Log.Debug call (DDLOG003 analyzer error)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…exists

When ExporterSettings hasn't pre-generated a pipe name, the integrations
now generate one via ServerlessCompatPipeNameHelper instead of falling back
to the compat layer's value. Also use realistic GUID-format pipe names in
test fixtures to match actual compat layer behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix "Compatability" → "Compatibility" typo (3 occurrences)
- Use `is null` / `is not null` instead of `== null` / `!= null`
- Use `StringUtil.IsNullOrEmpty()` instead of `string.IsNullOrEmpty()`
- Remove duplicate test file in Datadog.Trace.Tests
- Remove tests already covered by ServerlessCompatPipeNameHelperTests
- Fix SA1508 blank line before closing brace in PlatformKeys

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nique pipe generation

Instead of skipping pipe generation when an explicit pipe name is configured,
use the configured value as the base name with a GUID suffix appended. This
allows users to customize the pipe base name while still getting per-instance
isolation on shared Azure Functions hosting plans.

If no explicit pipe name is set, falls back to "dd_trace" / "dd_dogstatsd".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… integrations

CalculateDogStatsDPipeName and CalculateTracePipeName are each only called
once during Start(), so caching is unnecessary. The fallback to generate a
new unique name is also removed since ExporterSettings always pre-generates
the pipe name before these instrumentations fire, and generating a name the
tracer doesn't know about is pointless — fall back to the compat layer's
own value instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e constructor

Replace static field initializers that read env vars directly with instance
methods that use rawSettings.TracesPipeName/MetricsPipeName, respecting all
config sources (env vars, config files, etc.) instead of only environment
variables. Record the calculated pipe names in telemetry. Integrations now
read pipe names from Tracer.Instance.Settings.Exporter instead of removed
static properties.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e generation

Check the Azure Functions/compat layer condition once in the constructor
instead of duplicating it in two separate methods. Consolidate the two
per-pipe methods into a single GenerateUniquePipeName overload.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eHelper and make testable

Move compat layer detection out of ExporterSettings into
ServerlessCompatPipeNameHelper alongside the related pipe name logic.
Extract I/O dependencies (file existence, assembly version) as delegates
so version-checking logic can be fully unit tested. Add support for
DD_SERVERLESS_COMPAT_PATH env var to override the default binary path,
matching the compat layer's own behavior. Add comprehensive tests for
all branches: version checks, file missing, null version, exceptions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… fix ExporterSettings access path

- Add DD_SERVERLESS_COMPAT_PATH to supported-configurations.yaml so
  the source generator creates ConfigurationKeys.ServerlessCompatPath
- Fix integration classes to use the correct property path:
  Tracer.Instance.Settings.Manager.InitialExporterSettings
- Restore using Datadog.Trace.Configuration (needed for IntegrationId)
- Remove unused System.Reflection/InteropServices from ExporterSettings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
+ Maximum Version change

Co-authored-by: Andrew Lock <andrewlock.net@gmail.com>
StringUtil.IsNullOrEmpty has [NotNullWhen(false)] so the compiler
already knows the value is non-null in the false branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…SERVERLESS_COMPAT_PATH config key

Update supported_calltargets.g.json and generated_calltargets.g.cpp to
reflect MaximumVersion change from 2.*.* to 1.*.*. Add
DD_SERVERLESS_COMPAT_PATH to supported-configurations.yaml and restore
ConfigurationKeys usage in ServerlessCompatPipeNameHelper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Version 1.4.0 adds the CalculateTracePipeName and
CalculateDogStatsDPipeName methods that the tracer instruments
for named pipe coordination.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ests

- Update version range comments from 2.*.* to 1.*.* in both integrations
- Update DD_SERVERLESS_COMPAT_PATH documentation for linux default path
- Rewrite ServerlessCompatIntegrationTests: remove reflection-based tests
  for removed cache fields, add Theory-based tests for exception
  passthrough and fallback behavior with exact value assertions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…typo, use SkipOn in tests

- Move ServerlessCompatPipeNameHelper from ClrProfiler.AutoInstrumentation.Serverless
  to Datadog.Trace.Serverless to avoid Configuration depending on instrumentation code
- Fix "compatability" -> "compatibility" typo in YAML documentation
- Replace silent early-return platform guards with SkipOn/SkippableFact
  so Windows-only tests show as skipped instead of silently passing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Compat 1.4.0 isn't published yet and has a dependency on these tracer
changes. Keep the 1.2.0 dependency — the instrumentation silently skips
when the target methods don't exist, and IsCompatLayerAvailableWithPipeSupport
gates on >= 1.4.0 at runtime. The dependency will be bumped when compat
1.4.0 is published.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Master #8231 extracted serverless platform detection from
EnvironmentHelpers into dedicated cached classes under
Datadog.Trace.Serverless. Update the Azure-Functions pipe-name
generation gate to call AzureInfo.Instance.IsAzureFunction and
IsUsingSiteExtension instead of the removed EnvironmentHelpers
methods.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses review feedback: a customer-set TracesPipeName/MetricsPipeName
(from any configuration source) should pass through verbatim even when
running in Azure Functions, even though this can cause pipe-name
collisions across multiple function instances sharing a plan. We only
fabricate a GUID-suffixed default pipe name when the customer has not
configured one themselves.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Lewis-E Lewis-E force-pushed the lewis/SVLS-8244/add-multifunction-named-pipes branch from ceb49ad to db18e5a Compare April 21, 2026 17:18
Lewis-E and others added 3 commits April 21, 2026 14:24
…rebase

Post-rebase reconciliation: master's trace_filter additions took conflict
priority during rebase, dropping the ServerlessCompat integration_name tag
entries. Re-add them in both integration_name tag groups and shift downstream
indices by +2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous attempt missed `var index = N + ((int)tag1 * M) + (int)tag2;` forms.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Regenerated via Nuke (Restore + CompileManagedSrc). Both integration_name
tag groups now reflect 85 entries instead of 84.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@andrewlock andrewlock left a comment

Choose a reason for hiding this comment

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

Thanks! Mostly just a few nits but there's one scenario that I think is a potential bug, where the customer configures in code, we would re-generate a different pipe name, which might mean we inject the wrong value in the compat layer

// Use the tracer's configured pipe name, which in Azure Functions will be the
// unique name generated by ExporterSettings. Fall back to the compat layer's
// own calculated name if the tracer isn't initialized yet.
var pipeName = Tracer.Instance.Settings?.Manager?.InitialExporterSettings?.MetricsPipeName ?? returnValue;
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.

nit: none of these intermediate values can be null:

Suggested change
var pipeName = Tracer.Instance.Settings?.Manager?.InitialExporterSettings?.MetricsPipeName ?? returnValue;
var pipeName = Tracer.Instance.Settings.Manager.InitialExporterSettings.MetricsPipeName ?? returnValue;

return new CallTargetReturn<string>(returnValue);
}

try
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.

nit, tbh, It might be better to remove can probably remove the try-catch here. We catch exceptions escaping from here (and disable the integration) and we report it in telemetry, so that signal might be preferable. try-catch also means less chance of inlining, but this is a once-called thing, so not a big deal

Comment on lines +69 to +73
Log.Debug(
"ServerlessCompat integration: Overriding compat layer DogStatsD pipe name. " +
"Compat layer calculated: {CompatPipeName}, Tracer using: {TracerPipeName}",
returnValue,
pipeName);
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.

Technically you're not overriding the pipe name if MetricsPipeName is null here 😄

// Use the tracer's configured pipe name, which in Azure Functions will be the
// unique name generated by ExporterSettings. Fall back to the compat layer's
// own calculated name if the tracer isn't initialized yet.
var pipeName = Tracer.Instance.Settings?.Manager?.InitialExporterSettings?.TracesPipeName ?? returnValue;
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.

Same nits as the other instrumentation 🙂

string? tracesPipeName;
string? metricsPipeName;

if (AzureInfo.Instance.IsAzureFunction
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 wish we didn't access global state here 🙁 I'm a big proponent of injecting dependencies in the constructor if we need them.


if (version is null)
{
Log.Warning("Could not read Serverless Compatibility Layer details at {Path}, using fallback agent communication methods. (No Named Pipes)", compatDllPath);
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 don't know if this should be a warning? 🤔

Suggested change
Log.Warning("Could not read Serverless Compatibility Layer details at {Path}, using fallback agent communication methods. (No Named Pipes)", compatDllPath);
Log.Debug("Could not read Serverless Compatibility Layer details at {Path}, using fallback agent communication methods. (No Named Pipes)", compatDllPath);

string? metricsPipeName;

if (AzureInfo.Instance.IsAzureFunction
&& !AzureInfo.Instance.IsUsingSiteExtension
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.

We only want this code on Windows, right? We don't want named pipes on linux etc? If so, we should add that check first, as it's cheap

}
catch (Exception ex)
{
Log.Warning(ex, "Failed to determine Serverless Compatibility layer availability or Named Pipe Support.");
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 one probably should be an error, no? so that we get telemetry?

Suggested change
Log.Warning(ex, "Failed to determine Serverless Compatibility layer availability or Named Pipe Support.");
Log.Error(ex, "Failed to determine Serverless Compatibility layer availability or Named Pipe Support.");

[InlineData("dogstatsd", "dd_dogstatsd_from_compat_layer")]
public void OnMethodEnd_WhenTracerHasNoPipeName_FallsBackToReturnValue(string pipeType, string compatLayerName)
{
// In a unit test environment, Tracer.Instance won't have pipe names configured,
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.

In general, it's Strongly recommended you don't run code that relies on Tracer.Instance in unit tests. If you want to test this behaviour (which I'd argue would be better tested in an integration test instead), then I'd suggest extracting the code to a separate method, and testing that, and avoiding the Tracer.Instance call (which you really can't control here: insert standard thoughts on accessing global state 😄 )

// When no explicit pipe name is configured and not in Azure Functions,
// pipe transport is not used so MetricsPipeName should be null.
// In Azure Functions, the constructor generates a unique name which
// is covered by integration tests.
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'm not sure it is covered by integration tests 😄

I don't actually see any "real" integration tests, that does end-to-end with named pipes - do they live in a separate repo? Is there anything more we can add there?

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants