Skip to content

RUM-14843: Set active span ID as RUM resource parent span ID.#2726

Merged
simaoseica-dd merged 15 commits into
developfrom
marroz/rum-resource-parent-span-id
Mar 11, 2026
Merged

RUM-14843: Set active span ID as RUM resource parent span ID.#2726
simaoseica-dd merged 15 commits into
developfrom
marroz/rum-resource-parent-span-id

Conversation

@arroz

@arroz arroz commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

What and why?

Currently, when URLSession requests are instrumented by the RUM feature (through the use of the RUM.Configuration.urlSessionTracking configuration option) to generate a RUM Resource, that resource is augmented with a trace and span ID, both generated on the fly, and later used by the backend to synthesize an APM trace span from the RUM resource using those IDs. This is not ideal since that span will be detached from any other trace, breaking the continuity of the generated distributed trace.

This PR links both features: if there is a currently active span for the relevant activity, RUM will use it for augmenting the RUM resource. Specifically:

  • The active span's trace ID will be used as the RUM resource trace ID;
  • The active span's ID will be used as the RUM resource parent span ID;
  • The active span sampling information (priority and decision mechanism) will be used for the RUM resource.
  • The RUM resource span ID will still be created on the fly and propagated through the request headers, since any child spans created by the customer's backend tracers and agents need to be children of that ID.

The use of the active span's priority and decision mechanism implies the sampleRate part of the FirstPartyHostsTracing argument of urlSessionTracking will be ignored if there is an active span. As an example:

RUM.enable(
  with: RUM.Configuration(
    applicationID: appID,
    sessionSampleRate: 20,
    (…)
    urlSessionTracking: .init(
      firstPartyHostsTracing: .trace(
          hosts: ["example.com"],
          sampleRate: 10,  // 👈 This is ignored if there is an active span. It's still used otherwise.
          traceControlInjection: .sampled),
        resourceAttributesProvider: nil)
   )
)

As a reminder, even if the active span is not sampled, the RUM resource itself will be sampled if the RUM session is sampled. The only thing the active span sampling controls is if Datadog backend generates the APM trace span out of the RUM resource, or not.

How?

Trace feature initialization adds an instance of ActiveSpanProvider as additional context to the core. This entity contains a provider function that is used to dynamically obtain the active span.

The RUM feature creates an ActiveSpanProviderReceiver and registers it as a feature message receiver. When the context is updated, this receiver obtains the ActiveSpanProvider from the context and stores it. The instance of ActiveSpanProviderReceiver is also placed on the DistributedTracing instance, allowing URLSessionRUMResourcesHandler to finally obtain the active span and properly augment the RUM resource with it.

Notes

  • This is an "always on" feature as long as both RUM and Trace are enabled. A configuration option to turn this off was considered, but there is not an obvious use case where the users would want to disable this feature that justifies the added complexity of yet another configuration option.
  • The W3C trace context headers have a parent-id key. However, this key expects the RUM resource span ID, not its parent, the active span ID. This is confusing since the other header formats (Datadog and B3) both have explicit fields for the span and parent span IDs, implying the meaning of "parent" ID is different in the W3C case. This, however, makes sense, since the RUM resource span ID will be used as the parent span for any span created in the customers' backends.
  • The function that adds an additional context to the core runs asynchronously, so there's a small chance the context was not yet updated when RUM starts instrumenting requests. The probability of this happening is rather low, but this is something that cannot be avoided with the current architecture.

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
  • Add Objective-C interface for public APIs - see our guidelines (internal)
  • Run make api-surface when adding new APIs

@arroz
arroz force-pushed the marroz/rum-resource-parent-span-id branch from 8f0465a to 233f846 Compare March 5, 2026 11:53
@datadog-official

datadog-official Bot commented Mar 5, 2026

Copy link
Copy Markdown

✅ Tests

🎉 All green!

❄️ No new flaky tests detected
🧪 All tests passed

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: a0a81e2 | Docs | Datadog PR Page | Was this helpful? React with 👍/👎 or give us feedback!

@arroz
arroz marked this pull request as ready for review March 5, 2026 17:12
@arroz
arroz requested review from a team as code owners March 5, 2026 17:12
gonzalezreal
gonzalezreal previously approved these changes Mar 6, 2026

@gonzalezreal gonzalezreal 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.

👌🏽Great work! I think the approach is clean and fits well with the existing architecture.

As a suggestion, consider adding an integration test (in this PR or a follow-up) that enables both RUM and Trace, starts an active span, fires a request within that span's scope, and asserts the RUM resource carries the active span's trace/span IDs and sampling priority. The unit tests cover the logic, but an integration test would validate the full wiring end-to-end, from Trace.enable() registering the ActiveSpanProviderAdditionalContext, through ActiveSpanProviderReceiver picking it up, to DistributedTracing using it. The existing infrastructure (HTTPServerMock, RUMSessionMatcher, SpanMatcher) should make this possible.

@arroz

arroz commented Mar 9, 2026

Copy link
Copy Markdown
Contributor Author

I just pushed a new commit with Integration Tests.

arroz added 10 commits March 10, 2026 10:14
This commit adds the implementation of this functionality:
- A new mechanism was created for RUM to access the current active span from the Trace feature, if any.
- If an active span exists, RUM will use its trace ID, and the span ID as the parent span ID for the resource augmented info.

This allows the backend to attach the APM span synthesized from the RUM resource to the trace on the APM side, instead of creating a root span. It also ensures RUM propagates the correct trace ID headers so the trace is fully assembled on the backend.
…e previous commit.

For improved testability, `ActiveSpanProvider` and `ActiveSpanProviderReceiver` were abstracted by protocols so mocks can be used.
The priority is used exclusively for the conversion from RUM resource event to span on the backend (the RUM event itself is always sampled as long as the session itself is sampled). This commit makes the active span override the RUM resource sampler priority. This way, all the trace spans (including the one generated from RUM) will have the same priority, so either will all be sampled, or none will.
…ass.

This worked somewhat accidentally because of how the ReadWriteLock is implemented, but it was conceptually wrong, since we need to refer to the same instance from different places.
Since more message receivers need to listen to context changes, `ActiveSpanProviderReceiver` must return false from the receive(…) method.
@arroz
arroz force-pushed the marroz/rum-resource-parent-span-id branch from 8e54fc0 to 9634607 Compare March 10, 2026 10:15

@simaoseica-dd simaoseica-dd 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.

Left some comments since I see some room for improvement on the architecture.
Also, please add this description point to the RUM.Configuration since it changes behavior.

This is ignored if there is an active span. It's still used otherwise.

Comment thread DatadogRUM/Sources/Feature/RUMFeature.swift Outdated
Comment thread DatadogRUM/Sources/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift Outdated
Comment thread DatadogRUM/Sources/Integrations/ActiveSpanProviderReceiver.swift Outdated
Comment thread DatadogTrace/Sources/Trace.swift Outdated
Comment thread DatadogTrace/Sources/Trace.swift
Comment thread DatadogRUM/Sources/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift Outdated
@simaoseica-dd

Copy link
Copy Markdown
Contributor

@codex review

@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: 0dfc295d97

ℹ️ 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 DatadogRUM/Sources/Instrumentation/Resources/URLSessionRUMResourcesHandler.swift Outdated
arroz added 4 commits March 11, 2026 12:49
- Remove ActiveSpanProviderReceiver, and use NetworkContextProvider instead;
- Move ActiveSpanProvider to TraceCoreContext;
- Fix wrong sampling rate source.

@simaoseica-dd simaoseica-dd 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.

Well done! 🚀

@simaoseica-dd
simaoseica-dd merged commit 58a49df into develop Mar 11, 2026
17 checks passed
@simaoseica-dd
simaoseica-dd deleted the marroz/rum-resource-parent-span-id branch March 11, 2026 14:59
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.

3 participants