Skip to content

WCF cleanup and trace context extraction#8263

Merged
andrewlock merged 10 commits into
masterfrom
andrew/wcf-cleanup
Mar 12, 2026
Merged

WCF cleanup and trace context extraction#8263
andrewlock merged 10 commits into
masterfrom
andrew/wcf-cleanup

Conversation

@andrewlock

Copy link
Copy Markdown
Member

Summary of changes

Follow on fixes from WCF improvements made in #7842

Reason for change

@zacharycmontoya and I identified some things to fix while working on the above PR, but deferred fixing them till later. And now is that time! We flagged two main things:

  • We're using a weak table currently for storing the scopes. That's fine, but we always dispose the scope, we should just remove the object from the table at the same time to avoid accessing a closed span (and actively reducing the size of the table)
  • We should skip extracting WCF message headers if there's an active scope. Previously we were only doing that if we were working with http headers

While trying to create a repro for the second point (by using the Otel WCF instrumentation) 🤖 discovered a different issue, whereby the WCF server wcf.request spans were incorrectly parented under the manual span instead, of the OTel WCF client span dotnet_wcf.client.request for NetTcpBinding (when DD_TRACE_OTEL_ENABLED=true and OpenTelemetry WCF client instrumentation was active).

The latter issue was initially related to how we interact with the Otel instrumentation in the sample, but switching to "pure" OTel for propagation still revealed a parenting problem, because we were never extracting the OTel headers.

Note that this latter point only impacts the NetTcpBinding because with HTTP we handle all the propagation as expected.

Implementation details

There are 3 fixes:

  • (minor) Remove scope from weak table when we dispose it.
  • (minor) Don't try to extract propagation context in server-side WCF if there's already an active scope - couldn't repro, but it makes sense
  • (fix) Also check the W3C trace context namespace for headers injected

Test coverage

To increase coverage, reworked the Samples.Wcf to allow optionally using the Otel instrumentation:

  • Switch to combinatorial data (makes it easier to add another boolean)
  • Optionally allow setting up wcf client-side Otel instrumentation
    • When enabled, we configure a TraceProvider, add the TelemetryEndpointBehavior, and stop manually injecting our headers into the message
    • When disabled, the sample is identical to before
  • Update the snapshots
    • Rename the existing snapshots to include the additional useOtelClientInstrumentation=False suffix
    • Generate new snapshots for useOtelClientInstrumentation=True
    • Make the fix, and then update the snapshots in an additional commit, to demonstrate the change

Other details

Heavily used Claude Code on the testing and implementation. We knew about the first two issues, and it discovered the third when trying to follow our suggestion for a repro

@andrewlock
andrewlock requested review from a team as code owners March 3, 2026 15:17
@andrewlock andrewlock added type:bug area:automatic-instrumentation Automatic instrumentation managed C# code (Datadog.Trace.ClrProfiler.Managed) type:cleanup Minor code clean up AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos labels Mar 3, 2026

@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: 84a0ae0a48

ℹ️ 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/ClrProfiler/AutoInstrumentation/Wcf/WcfCommon.cs Outdated
@andrewlock
andrewlock requested a review from Copilot March 3, 2026 15:32

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

This PR applies follow-up fixes to WCF trace context handling (especially with OpenTelemetry WCF client instrumentation) and updates the integration tests/snapshots accordingly.

Changes:

  • Stop extracting WCF propagation context when a scope is already active, and additionally search for W3C Trace Context SOAP header namespace.
  • Remove disposed WCF server scopes from the weak-table to avoid referencing closed spans and reduce table size.
  • Rework Samples.Wcf integration tests to optionally use OTel client instrumentation and add new snapshots for that mode.

Reviewed changes

Copilot reviewed 16 out of 58 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Wcf/WcfCommon.cs Skips propagation extraction when a scope exists; adds W3C SOAP namespace fallback for header extraction
tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Wcf/BeforeSendReplyIntegration.cs Removes request scope from the scopes table when the reply is sent and the scope is disposed
tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WcfTests.cs Converts test matrix to combinatorial/pairwise and adds optional OTel client instrumentation toggle
tracer/test/snapshots/WcfTests.__metadataSchemaVersion=v0_binding=NetTcpBinding_enableNewWcfInstrumentation=True_enableWcfObfuscation=True_useOtelClientInstrumentation=True.verified.txt New snapshot covering NetTcpBinding + OTel client instrumentation (new WCF + obfuscation)
tracer/test/snapshots/WcfTests.__metadataSchemaVersion=v0_binding=NetTcpBinding_enableNewWcfInstrumentation=True_enableWcfObfuscation=False_useOtelClientInstrumentation=True.verified.txt New snapshot covering NetTcpBinding + OTel client instrumentation (new WCF + no obfuscation)
tracer/test/snapshots/WcfTests.__metadataSchemaVersion=v0_binding=NetTcpBinding_enableNewWcfInstrumentation=False_enableWcfObfuscation=True_useOtelClientInstrumentation=True.verified.txt New snapshot covering NetTcpBinding + OTel client instrumentation (old WCF + obfuscation)
tracer/test/snapshots/WcfTests.__metadataSchemaVersion=v0_binding=NetTcpBinding_enableNewWcfInstrumentation=False_enableWcfObfuscation=False_useOtelClientInstrumentation=True.verified.txt New snapshot covering NetTcpBinding + OTel client instrumentation (old WCF + no obfuscation)
tracer/test/snapshots/WcfTests.__metadataSchemaVersion=v0_binding=Custom_enableNewWcfInstrumentation=True_enableWcfObfuscation=True_useOtelClientInstrumentation=True.verified.txt New snapshot covering Custom binding + OTel client instrumentation
tracer/test/snapshots/WcfTests.__metadataSchemaVersion=v0_binding=BasicHttpBinding_enableNewWcfInstrumentation=True_enableWcfObfuscation=True_useOtelClientInstrumentation=True.verified.txt New snapshot covering BasicHttpBinding + OTel client instrumentation
Comments suppressed due to low confidence (3)

tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Wcf/WcfCommon.cs:75

  • The ActiveScope guard now wraps the entire request-property parsing block, which means when a scope is already active you also skip extracting request metadata (e.g., userAgent / httpMethod) that’s unrelated to propagation. Consider moving the guard so it only skips propagation context extraction (HTTP headers + WCF message headers), while still populating request metadata/tags regardless of an existing scope.
                var requestProperties = requestMessage.Properties;

                if (tracer.ActiveScope is { } activeScope)
                {
                    Log.Warning("Skipped extracting headers due to existing scope: {ActiveScope}", activeScope.Span);
                }
                else
                {
                    if (requestProperties is not null
                     && requestProperties.TryGetValue("httpRequest", out var httpRequestProperty)

tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Wcf/WcfCommon.cs:71

  • Logging this as Warning is likely to be noisy in normal scenarios where an upstream/manual/OTel scope exists (and skipping extraction is expected). Consider lowering to Debug (or using a rate-limited log) and/or logging a simpler identifier instead of the full span object to avoid excessive log volume and potentially expensive formatting.
                if (tracer.ActiveScope is { } activeScope)
                {
                    Log.Warning("Skipped extracting headers due to existing scope: {ActiveScope}", activeScope.Span);
                }

tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WcfTests.cs:78

  • The environment variable is only set when useOtelClientInstrumentation is true and is never explicitly unset/set to 0 when false. This can make tests order-dependent/flaky if the process environment leaks between test cases. Consider always setting it based on the boolean (or explicitly clearing it in the false branch).
            if (useOtelClientInstrumentation)
            {
                SetEnvironmentVariable("USE_OTEL_CLIENT_INSTRUMENTATION", "1");
            }

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

@dd-trace-dotnet-ci-bot

dd-trace-dotnet-ci-bot Bot commented Mar 3, 2026

Copy link
Copy Markdown

Execution-Time Benchmarks Report ⏱️

Execution-time results for samples comparing This PR (8263) 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
duration69.38 ± (69.42 - 69.68) ms69.38 ± (69.42 - 69.71) ms+0.0%✅⬆️
.NET Framework 4.8 - Bailout
duration73.28 ± (73.14 - 73.38) ms73.41 ± (73.36 - 73.65) ms+0.2%✅⬆️
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1040.04 ± (1041.37 - 1047.73) ms1040.12 ± (1044.18 - 1053.58) ms+0.0%✅⬆️
.NET Core 3.1 - Baseline
process.internal_duration_ms21.81 ± (21.78 - 21.84) ms21.87 ± (21.84 - 21.89) ms+0.3%✅⬆️
process.time_to_main_ms80.34 ± (80.19 - 80.49) ms79.98 ± (79.81 - 80.15) ms-0.4%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.95 ± (10.94 - 10.95) MB10.92 ± (10.92 - 10.93) MB-0.2%
runtime.dotnet.threads.count12 ± (12 - 12)12 ± (12 - 12)+0.0%
.NET Core 3.1 - Bailout
process.internal_duration_ms21.81 ± (21.78 - 21.85) ms21.81 ± (21.78 - 21.83) ms-0.0%
process.time_to_main_ms81.64 ± (81.52 - 81.76) ms81.06 ± (80.95 - 81.18) ms-0.7%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.98 ± (10.98 - 10.99) MB10.95 ± (10.94 - 10.95) MB-0.3%
runtime.dotnet.threads.count13 ± (13 - 13)13 ± (13 - 13)+0.0%
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms254.81 ± (252.37 - 257.26) ms252.35 ± (249.51 - 255.18) ms-1.0%
process.time_to_main_ms471.28 ± (470.72 - 471.84) ms468.18 ± (467.69 - 468.68) ms-0.7%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed47.71 ± (47.68 - 47.73) MB47.63 ± (47.60 - 47.65) MB-0.2%
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)+0.8%✅⬆️
.NET 6 - Baseline
process.internal_duration_ms20.69 ± (20.67 - 20.71) ms20.59 ± (20.56 - 20.61) ms-0.5%
process.time_to_main_ms69.57 ± (69.45 - 69.70) ms69.38 ± (69.22 - 69.53) ms-0.3%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.63 ± (10.63 - 10.63) MB10.63 ± (10.62 - 10.63) MB-0.0%
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 6 - Bailout
process.internal_duration_ms20.66 ± (20.63 - 20.69) ms20.51 ± (20.49 - 20.53) ms-0.7%
process.time_to_main_ms70.90 ± (70.78 - 71.03) ms70.31 ± (70.19 - 70.42) ms-0.8%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed10.74 ± (10.74 - 10.75) MB10.74 ± (10.74 - 10.74) MB-0.0%
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms253.55 ± (252.71 - 254.38) ms253.94 ± (252.90 - 254.98) ms+0.2%✅⬆️
process.time_to_main_ms449.61 ± (449.14 - 450.09) ms449.87 ± (449.24 - 450.50) ms+0.1%✅⬆️
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed48.37 ± (48.34 - 48.40) MB48.53 ± (48.50 - 48.57) MB+0.3%✅⬆️
runtime.dotnet.threads.count28 ± (28 - 28)28 ± (28 - 28)+0.0%
.NET 8 - Baseline
process.internal_duration_ms19.00 ± (18.97 - 19.02) ms18.83 ± (18.80 - 18.85) ms-0.9%
process.time_to_main_ms68.87 ± (68.75 - 68.99) ms68.42 ± (68.30 - 68.55) ms-0.7%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.68 ± (7.67 - 7.69) MB7.68 ± (7.67 - 7.69) MB-0.0%
runtime.dotnet.threads.count10 ± (10 - 10)10 ± (10 - 10)+0.0%
.NET 8 - Bailout
process.internal_duration_ms19.07 ± (19.05 - 19.10) ms18.95 ± (18.93 - 18.98) ms-0.6%
process.time_to_main_ms70.12 ± (70.00 - 70.24) ms69.53 ± (69.43 - 69.63) ms-0.8%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed7.73 ± (7.73 - 7.74) MB7.74 ± (7.73 - 7.74) MB+0.0%✅⬆️
runtime.dotnet.threads.count11 ± (11 - 11)11 ± (11 - 11)+0.0%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms180.60 ± (179.77 - 181.43) ms181.90 ± (181.02 - 182.77) ms+0.7%✅⬆️
process.time_to_main_ms431.62 ± (431.02 - 432.23) ms429.01 ± (428.42 - 429.61) ms-0.6%
runtime.dotnet.exceptions.count0 ± (0 - 0)0 ± (0 - 0)+0.0%
runtime.dotnet.mem.committed35.88 ± (35.86 - 35.91) MB35.96 ± (35.93 - 35.99) MB+0.2%✅⬆️
runtime.dotnet.threads.count27 ± (27 - 27)27 ± (27 - 27)-0.0%

HttpMessageHandler

Metric Master (Mean ± 95% CI) Current (Mean ± 95% CI) Change Status
.NET Framework 4.8 - Baseline
duration195.37 ± (195.52 - 196.37) ms195.31 ± (195.04 - 195.88) ms-0.0%
.NET Framework 4.8 - Bailout
duration199.13 ± (198.86 - 199.56) ms198.97 ± (198.56 - 199.13) ms-0.1%
.NET Framework 4.8 - CallTarget+Inlining+NGEN
duration1153.07 ± (1157.06 - 1166.46) ms1151.25 ± (1154.48 - 1162.17) ms-0.2%
.NET Core 3.1 - Baseline
process.internal_duration_ms190.59 ± (190.13 - 191.05) ms189.51 ± (189.10 - 189.92) ms-0.6%
process.time_to_main_ms83.16 ± (82.92 - 83.39) ms82.15 ± (81.94 - 82.36) ms-1.2%
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.12 ± (16.09 - 16.14) MB16.05 ± (16.03 - 16.08) MB-0.4%
runtime.dotnet.threads.count20 ± (20 - 20)20 ± (19 - 20)-0.6%
.NET Core 3.1 - Bailout
process.internal_duration_ms189.24 ± (188.85 - 189.64) ms188.49 ± (188.16 - 188.82) ms-0.4%
process.time_to_main_ms83.98 ± (83.79 - 84.16) ms83.55 ± (83.38 - 83.72) ms-0.5%
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed16.13 ± (16.10 - 16.15) MB16.15 ± (16.12 - 16.18) MB+0.1%✅⬆️
runtime.dotnet.threads.count21 ± (21 - 21)21 ± (21 - 21)+0.0%✅⬆️
.NET Core 3.1 - CallTarget+Inlining+NGEN
process.internal_duration_ms432.08 ± (428.70 - 435.47) ms431.12 ± (427.73 - 434.51) ms-0.2%
process.time_to_main_ms478.59 ± (477.97 - 479.22) ms481.31 ± (480.48 - 482.13) ms+0.6%✅⬆️
runtime.dotnet.exceptions.count3 ± (3 - 3)3 ± (3 - 3)+0.0%
runtime.dotnet.mem.committed58.10 ± (57.99 - 58.22) MB58.10 ± (57.99 - 58.22) MB-0.0%
runtime.dotnet.threads.count29 ± (29 - 29)29 ± (29 - 29)-0.1%
.NET 6 - Baseline
process.internal_duration_ms193.86 ± (193.51 - 194.21) ms192.87 ± (192.50 - 193.24) ms-0.5%
process.time_to_main_ms71.66 ± (71.47 - 71.84) ms71.20 ± (71.06 - 71.35) ms-0.6%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.37 ± (16.34 - 16.41) MB15.97 ± (15.82 - 16.12) MB-2.5%
runtime.dotnet.threads.count19 ± (19 - 19)18 ± (18 - 19)-3.8%
.NET 6 - Bailout
process.internal_duration_ms193.17 ± (192.80 - 193.54) ms192.55 ± (192.13 - 192.97) ms-0.3%
process.time_to_main_ms72.51 ± (72.35 - 72.66) ms72.38 ± (72.24 - 72.53) ms-0.2%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed16.27 ± (16.15 - 16.38) MB16.29 ± (16.18 - 16.40) MB+0.1%✅⬆️
runtime.dotnet.threads.count20 ± (20 - 20)20 ± (20 - 20)-0.2%
.NET 6 - CallTarget+Inlining+NGEN
process.internal_duration_ms454.29 ± (452.77 - 455.81) ms451.29 ± (449.19 - 453.40) ms-0.7%
process.time_to_main_ms453.63 ± (453.02 - 454.23) ms454.69 ± (454.01 - 455.37) ms+0.2%✅⬆️
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed57.90 ± (57.79 - 58.01) MB58.15 ± (58.03 - 58.27) MB+0.4%✅⬆️
runtime.dotnet.threads.count29 ± (29 - 29)29 ± (29 - 29)+0.1%✅⬆️
.NET 8 - Baseline
process.internal_duration_ms192.45 ± (192.02 - 192.88) ms191.36 ± (191.01 - 191.71) ms-0.6%
process.time_to_main_ms70.87 ± (70.67 - 71.08) ms70.71 ± (70.53 - 70.90) ms-0.2%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.73 ± (11.71 - 11.76) MB11.75 ± (11.73 - 11.77) MB+0.1%✅⬆️
runtime.dotnet.threads.count18 ± (18 - 18)18 ± (18 - 18)-0.2%
.NET 8 - Bailout
process.internal_duration_ms192.31 ± (191.90 - 192.72) ms190.80 ± (190.37 - 191.22) ms-0.8%
process.time_to_main_ms72.03 ± (71.89 - 72.17) ms71.97 ± (71.80 - 72.14) ms-0.1%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed11.81 ± (11.78 - 11.84) MB11.80 ± (11.77 - 11.83) MB-0.1%
runtime.dotnet.threads.count19 ± (19 - 19)19 ± (19 - 19)-0.1%
.NET 8 - CallTarget+Inlining+NGEN
process.internal_duration_ms368.65 ± (367.24 - 370.06) ms370.16 ± (368.67 - 371.64) ms+0.4%✅⬆️
process.time_to_main_ms438.49 ± (437.84 - 439.15) ms438.29 ± (437.54 - 439.03) ms-0.0%
runtime.dotnet.exceptions.count4 ± (4 - 4)4 ± (4 - 4)+0.0%
runtime.dotnet.mem.committed47.76 ± (47.72 - 47.79) MB47.80 ± (47.77 - 47.84) MB+0.1%✅⬆️
runtime.dotnet.threads.count29 ± (29 - 29)29 ± (29 - 29)-0.2%
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 (8263) - mean (70ms)  : 68, 72
    master - mean (70ms)  : 68, 71

    section Bailout
    This PR (8263) - mean (74ms)  : 72, 75
    master - mean (73ms)  : 72, 74

    section CallTarget+Inlining+NGEN
    This PR (8263) - mean (1,049ms)  : 982, 1116
    master - mean (1,045ms)  : 999, 1090

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 (8263) - mean (107ms)  : 105, 110
    master - mean (108ms)  : 105, 111

    section Bailout
    This PR (8263) - mean (108ms)  : 107, 110
    master - mean (109ms)  : 107, 112

    section CallTarget+Inlining+NGEN
    This PR (8263) - mean (744ms)  : 690, 797
    master - mean (762ms)  : 723, 801

Loading
FakeDbCommand (.NET 6)
gantt
    title Execution time (ms) FakeDbCommand (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8263) - mean (95ms)  : 92, 98
    master - mean (95ms)  : 93, 98

    section Bailout
    This PR (8263) - mean (96ms)  : 94, 98
    master - mean (97ms)  : 95, 99

    section CallTarget+Inlining+NGEN
    This PR (8263) - mean (729ms)  : 700, 757
    master - mean (741ms)  : 721, 761

Loading
FakeDbCommand (.NET 8)
gantt
    title Execution time (ms) FakeDbCommand (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8263) - mean (94ms)  : 91, 96
    master - mean (95ms)  : 92, 98

    section Bailout
    This PR (8263) - mean (95ms)  : 93, 97
    master - mean (96ms)  : 94, 98

    section CallTarget+Inlining+NGEN
    This PR (8263) - mean (637ms)  : 623, 651
    master - mean (650ms)  : 631, 669

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 (8263) - mean (195ms)  : 191, 200
    master - mean (196ms)  : 192, 200

    section Bailout
    This PR (8263) - mean (199ms)  : 196, 202
    master - mean (199ms)  : 196, 203

    section CallTarget+Inlining+NGEN
    This PR (8263) - mean (1,158ms)  : 1101, 1216
    master - mean (1,162ms)  : 1090, 1234

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 (8263) - mean (280ms)  : 275, 286
    master - mean (283ms)  : 275, 291

    section Bailout
    This PR (8263) - mean (281ms)  : 277, 285
    master - mean (282ms)  : 277, 287

    section CallTarget+Inlining+NGEN
    This PR (8263) - mean (946ms)  : 896, 996
    master - mean (946ms)  : 902, 989

Loading
HttpMessageHandler (.NET 6)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 6)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8263) - mean (273ms)  : 268, 277
    master - mean (274ms)  : 268, 280

    section Bailout
    This PR (8263) - mean (273ms)  : 268, 278
    master - mean (274ms)  : 269, 279

    section CallTarget+Inlining+NGEN
    This PR (8263) - mean (935ms)  : 901, 969
    master - mean (942ms)  : 908, 975

Loading
HttpMessageHandler (.NET 8)
gantt
    title Execution time (ms) HttpMessageHandler (.NET 8)
    dateFormat  x
    axisFormat %Q
    todayMarker off
    section Baseline
    This PR (8263) - mean (272ms)  : 268, 276
    master - mean (273ms)  : 265, 282

    section Bailout
    This PR (8263) - mean (273ms)  : 265, 281
    master - mean (274ms)  : 270, 279

    section CallTarget+Inlining+NGEN
    This PR (8263) - mean (840ms)  : 817, 864
    master - mean (839ms)  : 815, 863

Loading

@zacharycmontoya zacharycmontoya left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, thanks!

@pr-commenter

pr-commenter Bot commented Mar 3, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-03-03 18:20:06

Comparing candidate commit c8e4479 in PR branch andrew/wcf-cleanup with baseline commit 3e34fe2 in branch master.

Found 9 performance improvements and 7 performance regressions! Performance is the same for 163 metrics, 13 unstable metrics.

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

  • 🟩 execution_time [-108.180ms; -105.717ms] or [-54.017%; -52.788%]

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

  • 🟩 execution_time [-84.787ms; -84.681ms] or [-40.832%; -40.781%]

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

  • 🟥 throughput [-10750.421op/s; -10386.131op/s] or [-7.331%; -7.082%]

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

  • 🟩 throughput [+416.107op/s; +449.686op/s] or [+6.369%; +6.883%]

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

  • 🟩 throughput [+525.139op/s; +1352.789op/s] or [+5.489%; +14.141%]

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

  • 🟩 execution_time [-15.427ms; -10.349ms] or [-8.691%; -5.830%]

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

  • 🟩 throughput [+128.978op/s; +183.378op/s] or [+8.493%; +12.075%]

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

  • 🟥 throughput [-202.931op/s; -150.064op/s] or [-38.271%; -28.301%]

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

  • 🟥 throughput [-59015.331op/s; -46330.756op/s] or [-8.800%; -6.908%]

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

  • 🟥 execution_time [+10.729ms; +15.508ms] or [+5.444%; +7.868%]

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

  • 🟩 execution_time [-32.258ms; -28.028ms] or [-16.060%; -13.954%]

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

  • 🟩 throughput [+22052.802op/s; +28328.954op/s] or [+5.528%; +7.101%]

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

  • 🟥 execution_time [+101.610ms; +103.588ms] or [+104.519%; +106.554%]

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

  • 🟩 throughput [+15183926.000op/s; +16837467.679op/s] or [+6.735%; +7.469%]

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

  • 🟥 execution_time [+13.055ms; +17.224ms] or [+6.546%; +8.636%]

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

  • 🟥 execution_time [+12.383ms; +15.896ms] or [+6.368%; +8.174%]

@andrewlock
andrewlock merged commit bb7b839 into master Mar 12, 2026
139 checks passed
@andrewlock
andrewlock deleted the andrew/wcf-cleanup branch March 12, 2026 11:32
@github-actions github-actions Bot added this to the vNext-v3 milestone Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Generated Largely based on code generated by an AI or LLM. This label is the same across all dd-trace-* repos area:automatic-instrumentation Automatic instrumentation managed C# code (Datadog.Trace.ClrProfiler.Managed) type:bug type:cleanup Minor code clean up

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants