Skip to content

Refactoring and improvements to HttpHeaderHelpers#8153

Merged
andrewlock merged 11 commits intomasterfrom
andrew/header-helper-tweaks
Feb 9, 2026
Merged

Refactoring and improvements to HttpHeaderHelpers#8153
andrewlock merged 11 commits intomasterfrom
andrew/header-helper-tweaks

Conversation

@andrewlock
Copy link
Member

@andrewlock andrewlock commented Feb 4, 2026

Summary of changes

Various usability and performance improvements to HttpHeaderHelper

Reason for change

HttpHeaderHelper is used in conjunction with AgentTransportStrategy, for the cases where we're using our custom DatadogHttpClient instead of the built-in HttpClient etc. However, it's a bit clunky to use, and the way we're doing things at the moment causes a bunch of additional allocations on startup which we can avoid.

The PR is a linear set of self-contained commits, so feel free to either review one-by-one, or just go for it and review the whole lot.

Implementation details

  • If the header values are constants, we can pre-concatenate them all as a single constant at compile time, instead of building it up dynamically at runtime (with all the associated intermediate allocations)
  • Split the "container ID minimal headers" into its own type, so we can optimize these separately and more clearly
  • We were doing a "poor man's lazy" for the header calculation, but we can just use a normal Lazy<T> for this and accept it. We want to be lazy because we only ever call this property if we're using our customer client
  • Use StringBuilder to build the lazy values instead of linq and interpolation
  • Turn the helpers into singletons, and just pass those in instead of Func<T> calls, to reduce some allocations
  • Don't pass in uri => uri lambdas as pointless allocation that we can handle elsewhere
  • Instead of having to pass in an HttpHeaderHelper and a collection of default headers, expose the default headers directly on HttpHeaderHelper, as these must always stay in sync anyway.
  • Fix bug in ExposureApi that wasn't matching the two sets of headers it sent (see above point!)
  • Remove ContentType property from HttpHeaderHelperBase as it's never used
  • Delete MultipartAgentHeaderHelper, as it's now identical to MinimalAgentHeaderHelper
  • Rename MetadataHeaders to HttpSerializedDefaultHeaders (as more descriptive)

Test coverage

Added some unit tests to ensure all the HttpHeaderHelper implementations return the same headers (and the expected values) whether using the DefaultHeaders property or the private HttpSerializedDefaultHeaders property

@andrewlock andrewlock force-pushed the andrew/header-helper-tweaks branch from 904a182 to fe76568 Compare February 4, 2026 12:36
@andrewlock

This comment was marked as outdated.

@andrewlock andrewlock force-pushed the andrew/header-helper-tweaks branch from fe76568 to ec79d12 Compare February 4, 2026 13:15
@andrewlock andrewlock added type:refactor type:performance Performance, speed, latency, resource usage (CPU, memory) labels Feb 4, 2026
@andrewlock andrewlock marked this pull request as ready for review February 4, 2026 13:27
@andrewlock andrewlock requested review from a team as code owners February 4, 2026 13:27
@andrewlock andrewlock requested a review from sameerank February 4, 2026 13:27
Copy link
Member

@tonyredondo tonyredondo left a comment

Choose a reason for hiding this comment

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

nice to se a lot of new being removed.

Copy link
Contributor

@daniel-romano-DD daniel-romano-DD left a comment

Choose a reason for hiding this comment

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

Thanks a lot!

@pr-commenter
Copy link

pr-commenter bot commented Feb 4, 2026

Benchmarks

Benchmark execution time: 2026-02-04 14:09:05

Comparing candidate commit dc55da9 in PR branch andrew/header-helper-tweaks with baseline commit 7eb288c in branch master.

Found 5 performance improvements and 11 performance regressions! Performance is the same for 158 metrics, 18 unstable metrics.

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

  • 🟥 execution_time [+81.746ms; +81.900ms] or [+67.635%; +67.762%]

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

  • 🟥 execution_time [+12.262ms; +16.170ms] or [+6.101%; +8.046%]

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

  • 🟩 execution_time [-20.454ms; -14.811ms] or [-9.447%; -6.841%]

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

  • 🟩 execution_time [-24.191ms; -23.832ms] or [-11.940%; -11.763%]

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

  • 🟩 execution_time [-39.798ms; -32.378ms] or [-18.713%; -15.224%]

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

  • 🟥 execution_time [+153.352µs; +159.674µs] or [+5.518%; +5.746%]
  • 🟥 throughput [-19.562op/s; -18.806op/s] or [-5.437%; -5.226%]

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

  • 🟩 execution_time [-67.351µs; -62.369µs] or [-5.900%; -5.464%]
  • 🟩 throughput [+50.769op/s; +54.773op/s] or [+5.795%; +6.252%]

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

  • 🟥 execution_time [+2.392µs; +7.016µs] or [+5.089%; +14.926%]

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

  • 🟥 execution_time [+24.616ms; +25.660ms] or [+14.087%; +14.684%]

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

  • 🟥 execution_time [+20.601ms; +25.570ms] or [+10.315%; +12.803%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishSpan net472

  • 🟥 throughput [-97047.773op/s; -96556.344op/s] or [-8.643%; -8.600%]

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

  • 🟥 execution_time [+13.723ms; +19.377ms] or [+7.059%; +9.967%]

scenario:Benchmarks.Trace.SpanBenchmark.StartFinishTwoScopes net472

  • 🟥 throughput [-27815.888op/s; -26286.770op/s] or [-5.992%; -5.662%]

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

  • 🟥 execution_time [+10.833ms; +16.250ms] or [+5.457%; +8.185%]

new KeyValuePair<string, string>(TelemetryConstants.ClientLibraryVersionHeader, TracerConstants.AssemblyVersion),
new KeyValuePair<string, string>(HttpHeaderNames.TracingEnabled, "false"), // don't add automatic instrumentation to requests directed to the agent
};
=>
Copy link
Collaborator

Choose a reason for hiding this comment

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

In this case, we are creating new values for every call, right? Maybe this would be better?

internal static KeyValuePair<string, string>[] DefaultAgentHeaders { get; } =
[
new(TelemetryConstants.ClientLibraryLanguageHeader, TracerConstants.Language),
new(TelemetryConstants.ClientLibraryVersionHeader, TracerConstants.AssemblyVersion),
new(HttpHeaderNames.TracingEnabled, "false"),
];

Copy link
Collaborator

Choose a reason for hiding this comment

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

BTW, not in this PR but DataStreamsHttpHeaderNames also creates new values for each GetDefaultAgentHeaders call

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, I actually had the same thought, and did that originally, but changed my mind 😅

I decided against the property + initializer because then it will always be created, even if we never invoke this method, causing uneccessary allocation. And in practice, we only call this method once, so it shouldn't be an issue. That's especially enforced by making the types that use them singletons 😄

@dudikeleti dudikeleti self-requested a review February 9, 2026 14:38
Copy link
Contributor

@dudikeleti dudikeleti left a comment

Choose a reason for hiding this comment

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

Thanks!

@andrewlock andrewlock merged commit b4baf0a into master Feb 9, 2026
145 checks passed
@andrewlock andrewlock deleted the andrew/header-helper-tweaks branch February 9, 2026 17:55
@github-actions github-actions bot added this to the vNext-v3 milestone Feb 9, 2026
@andrewlock andrewlock added the area:tracer The core tracer library (Datadog.Trace, does not include OpenTracing, native code, or integrations) label Feb 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:tracer The core tracer library (Datadog.Trace, does not include OpenTracing, native code, or integrations) type:performance Performance, speed, latency, resource usage (CPU, memory) type:refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants