Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RUM-3470 feat: Head-based sampling for manual distributed tracing #1793

Conversation

ncreated
Copy link
Member

@ncreated ncreated commented Apr 24, 2024

What and why?

πŸ“¦ This PR continues the efforts from #1783 and earlier #1772. It adds support to head-based sampling for distributed traces created manually with Tracer API:

  • βœ… Distributed trace sent via Tracer API (*HTTPHeadersWriter):
client-ios-app:     [------ network.span -------]
client backend:         [--- backend span ---]
  • βœ… Distributed trace sent via Tracer API (*HTTPHeadersWriter) with implicit parent:
client-ios-app:     [------ active.span -------]
client-ios-app:       [---- network.span ----]
client backend:         [-- backend span --]

How?

In head-based sampling mode, headers must be sampled the same as the injected span context (spanContext.isKept). However, this contradicts with existing init(sampleRate:...) constructors. To accommodate this change, a new initializer (init(samplingStrategy: TraceSamplingStrategy)) was added to each headers writer. Additionally, existing constructors have been deprecated to encourage migration. Users should now consciously choose between arbitrary sampling or the new head-based approach:

// in HTTPHeadersWriter:
public init(sampleRate: Float = 20) // 🟨 now: deprecated
public init(samplingStrategy: TraceSamplingStrategy)


// in B3HTTPHeadersWriter:
public init(sampleRate: Float = 20, injectEncoding: InjectEncoding = .single) // 🟨 now: deprecated
public init(samplingStrategy: TraceSamplingStrategy, injectEncoding: InjectEncoding = .single) 

// in W3CHTTPHeadersWriter:
public init(sampleRate: Float = 20, tracestate: [String: String] = [:]) // 🟨 now: deprecated
public init(samplingStrategy: TraceSamplingStrategy, tracestate: [String: String] = [:])

The TraceSamplingStrategy enum enables both the new behavior (.auto) and the previous one (.custom(sampleRate:)):

/// Available strategies for sampling trace propagation headers.
public enum TraceSamplingStrategy {
    /// Trace propagation headers will be sampled same as propagated span.
    ///
    /// Use this option to leverage head-based sampling, where the decision to keep or drop the trace
    /// is determined from the first span of the trace, the head, when the trace is created. With `.auto`
    /// strategy, this decision is propagated through the request context to downstream services.
    case auto
    /// Trace propagation headers will be sampled independently from sampling decision in propagated span.
    ///
    /// Use this option to apply the provided `sampleRate` for determining the decision to keep or drop the trace
    /// in downstream services independently of sampling their parent span.
    case custom(sampleRate: Float)
}

πŸ’‘ Alternatives Considered

Another option was introducing new types for head-based sampling, but this approach would require solving the RUM-3535: Trace Context injection control issue first. It would simplify things, notably the uniformity of setting trace propagation headers across writers which varies:

Adopting head-based sampling in existing writers was chosen to avoid this complexity.

Review checklist

  • Feature or bugfix MUST have appropriate tests (unit, integration)
  • Make sure each commit and the PR mention the Issue number or JIRA reference
  • Add CHANGELOG entry for user facing changes

Custom CI job configuration (optional)

  • Run unit tests for Core, RUM, Trace, Logs, CR and WVT
  • Run unit tests for Session Replay
  • Run integration tests
  • Run smoke tests
  • Run tests for tools/

@datadog-datadog-prod-us1
Copy link

datadog-datadog-prod-us1 bot commented Apr 24, 2024

Datadog Report

Branch report: ncreated/RUM-3470/head-based-sampling-part2-with-new-init
Commit report: 8ec55a4
Test service: dd-sdk-ios

βœ… 0 Failed, 3016 Passed, 0 Skipped, 11m 35.89s Wall Time
πŸ”» Test Sessions change in coverage: 12 decreased, 2 increased

πŸ”» Code Coverage Decreases vs Default Branch (12)

This report shows up to 5 code coverage decreases.

  • test DatadogRUMTests tvOS 80.66% (-0.37%) - Details
  • test DatadogLogsTests tvOS 45.05% (-0.32%) - Details
  • test DatadogLogsTests iOS 45.01% (-0.31%) - Details
  • test DatadogCoreTests tvOS 79.37% (-0.25%) - Details
  • test DatadogCrashReportingTests iOS 27.68% (-0.21%) - Details

@ncreated ncreated force-pushed the ncreated/RUM-3470/head-based-sampling-part2-with-new-init branch from f93f17e to d8c2f2b Compare April 24, 2024 09:41
@ncreated ncreated self-assigned this Apr 24, 2024
Comment on lines 727 to -731

func testItInjectsSpanContextWithHTTPHeadersWriter() {
Trace.enable(with: config, in: core)
let tracer = Tracer.shared(in: core)
let spanContext1 = DDSpanContext(traceID: .init(idHi: 10, idLo: 100), spanID: 200, parentSpanID: .mockAny(), baggageItems: .mockAny(), sampleRate: .mockRandom(), isKept: .mockRandom())
Copy link
Member Author

Choose a reason for hiding this comment

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

🧹 I'm removing these tests from integration layer. Headers encoding / decoding was moved to DatadogTrace unit tests. This includes adding missing UT coverage for HTTPHeadersWriter.

@ncreated ncreated marked this pull request as ready for review April 24, 2024 10:59
@ncreated ncreated requested review from a team as code owners April 24, 2024 10:59
@ncreated ncreated mentioned this pull request Apr 24, 2024
8 tasks
@ncreated
Copy link
Member Author

ncreated commented Apr 24, 2024

blocking/ Note to myself:

  • Port this new API to objective-C

Copy link
Contributor

@ganeshnj ganeshnj left a comment

Choose a reason for hiding this comment

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

Neatly done @ncreated :kudos

@@ -162,33 +161,29 @@ extension DistributedTracing {
let writer: TracePropagationHeadersWriter
switch $0 {
case .datadog:
writer = HTTPHeadersWriter(sampler: deterministicSampler)
writer = HTTPHeadersWriter(samplingStrategy: .auto)
Copy link
Contributor

Choose a reason for hiding this comment

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

Changing the default to auto/head is behavior breaking change. We must make sure this in our changelog.

Copy link
Member Author

@ncreated ncreated Apr 25, 2024

Choose a reason for hiding this comment

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

@ganeshnj There should be no breaking change here, at least in my best efforts πŸ™‚. Please note that .auto here implies "derive sampling decision (0|1) from injected span context". The injectedSpanContext is sampled with the same sampler that was used before:

let injectedSpanContext = TraceContext(
   // ...
   sampleRate: sampler.samplingRate,
   isKept: sampler.sample()
)

Let me know WDYT or if I'm missing something.

Copy link
Contributor

Choose a reason for hiding this comment

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

here injected span context is basically span context of active span (Tracer) which we were not considering before while sampling the distributed trace (URLSessionTracking), which could mean more traces depending on the sampling rate defined at Trace level.

Copy link
Member Author

Choose a reason for hiding this comment

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

I see. So this regards changes made to TracingURLSessionHandler - RUM handler is backward compatible πŸ‘Œ. I will add a note on this in the consolidation PR (#1794).

@@ -32,23 +32,16 @@ public struct Sampler: Sampling {
}

/// A sampler that determines sampling decisions deterministically (the same each time).
public struct DeterministicSampler: Sampling {
internal struct DeterministicSampler: Sampling {
Copy link
Contributor

Choose a reason for hiding this comment

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

πŸ’ͺ

@ncreated ncreated merged commit cdd181f into ncreated/RUM-3470/head-based-sampling Apr 26, 2024
8 checks passed
@ncreated ncreated deleted the ncreated/RUM-3470/head-based-sampling-part2-with-new-init branch April 26, 2024 10:56
@maciejburda maciejburda mentioned this pull request May 7, 2024
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants