Skip to content

RUM-15139: Use rebased sessionReplaySampleRate for deterministic Session Replay sampling#3372

Merged
hamorillo merged 2 commits into
developfrom
hector.morilloprieto/RUM-15139
Apr 24, 2026
Merged

RUM-15139: Use rebased sessionReplaySampleRate for deterministic Session Replay sampling#3372
hamorillo merged 2 commits into
developfrom
hector.morilloprieto/RUM-15139

Conversation

@hamorillo

@hamorillo hamorillo commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Replaces the independent random Session Replay sampling with deterministic sampling based on the RUM session sample rate. Instead of two independent coin flips (RUM + SR), the SR sampling decision now uses a DeterministicSampler with a combined rate (sessionSampleRate * srSampleRate / 100), keyed on the session ID. This guarantees that any session sampled in for SR is also sampled in for RUM.

Key changes:

  • RumSessionScope: Sends sessionSampleRate (instead of keepSession) to Session Replay via the inter-feature bus
  • SessionReplayFeature: Replaces RateBasedSampler with DeterministicSampler using combinedSampleRate(). Removes the keepSession gate — it's now redundant because the combined threshold is always <= the RUM threshold
  • DeterministicSampling.combinedSampleRate(): New utility in dd-sdk-android-internal (equivalent to iOS's SampleRate.composed(with:)) for computing the effective rate when composing parent + child sampling
  • SessionSamplingIdProvider: Moved from dd-sdk-android-rum to dd-sdk-android-internal so it can be shared across features

Motivation

RUM-15139. Aligns Android SR sampling with the deterministic approach used by the iOS SDK (dd-sdk-ios#2714). The previous random sampler meant SR decisions were independent of RUM decisions, requiring the keepSession flag as a gate. With deterministic sampling, the math guarantees consistency — making keepSession unnecessary and simplifying the code.

Additional Notes

E2E Verification — Deterministic SR Sampling

The deterministic sampling decision can be verified on a real device using the android-sdk-event-inspection skill. Ask Claude Code to:

Using the android-sdk-event-inspection skill, modify the sample app to use RUM sample rate X% and SR sample rate Y%, capture the session ID and session_sample_rate/session_replay_sample_rate from the RUM view events, then verify that the has_replay field matches the expected deterministic sampling decision using the Knuth hash formula. Revert all changes when done.

Tested with 8 sessions across 5 rate combinations — all matched the Knuth hash math:

Session ID RUM Rate SR Rate Combined Hash Position Expected Observed
e3bbb091-4cb2-444b-8c00-5b45b410a3a5 80% 75% 60% 69.55% OUT has_replay: false
4d8b7e7b-5b29-45af-8381-fdaeaf470fd0 50% 40% 20% 36.16% OUT has_replay: false
c5254296-9dd5-4f27-9f9d-3cccddfaa227 90% 30% 27% 94.92% OUT (RUM) no RUM events
73edb0a2-ede8-4b6f-8c6f-f16c95f0fcbd 60% 50% 30% 50.43% OUT has_replay: false
735ba6e4-4ea0-4a9a-8505-1bbebd3cea31 80% 80% 64% 32.95% IN has_replay: true
9ba59950-fa30-459e-bdd5-20b1b8140d5b 80% 80% 64% 97.68% OUT (RUM) no RUM events
11274730-b7fd-4982-b426-185c176d9413 80% 80% 64% 89.48% OUT (RUM) no RUM events
ddd7b75d-ecc8-4ef7-a4c2-fc34db9230b1 80% 80% 64% 61.46% IN has_replay: true

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

@hamorillo

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

ℹ️ 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".

@hamorillo
hamorillo marked this pull request as ready for review April 16, 2026 08:16
@hamorillo
hamorillo requested review from a team as code owners April 16, 2026 08:16

@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: 2dbea616fb

ℹ️ 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".

@hamorillo
hamorillo force-pushed the hector.morilloprieto/RUM-15138 branch from 8f392fd to 0f30157 Compare April 16, 2026 09:05
@hamorillo
hamorillo force-pushed the hector.morilloprieto/RUM-15139 branch from 2dbea61 to 1217c89 Compare April 16, 2026 10:22
@codecov-commenter

codecov-commenter commented Apr 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.32258% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.97%. Comparing base (9d17b2a) to head (294bfe6).
⚠️ Report is 10 commits behind head on develop.

Files with missing lines Patch % Lines
...oid/internal/sampling/SessionSamplingIdProvider.kt 75.00% 0 Missing and 1 partial ⚠️
...droid/rum/internal/domain/scope/RumSessionScope.kt 83.33% 0 Missing and 1 partial ⚠️
...oid/sessionreplay/internal/SessionReplayFeature.kt 94.74% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #3372      +/-   ##
===========================================
- Coverage    72.03%   71.97%   -0.06%     
===========================================
  Files          956      957       +1     
  Lines        35307    35311       +4     
  Branches      5861     5859       -2     
===========================================
- Hits         25432    25415      -17     
- Misses        8271     8280       +9     
- Partials      1604     1616      +12     
Files with missing lines Coverage Δ
...android/internal/sampling/DeterministicSampling.kt 100.00% <100.00%> (ø)
...rum/src/main/kotlin/com/datadog/android/rum/Rum.kt 86.08% <ø> (ø)
.../datadog/android/rum/internal/domain/RumContext.kt 100.00% <ø> (ø)
...oid/internal/sampling/SessionSamplingIdProvider.kt 75.00% <75.00%> (ø)
...droid/rum/internal/domain/scope/RumSessionScope.kt 91.03% <83.33%> (+0.06%) ⬆️
...oid/sessionreplay/internal/SessionReplayFeature.kt 98.27% <94.74%> (-0.58%) ⬇️

... and 40 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

kikoveiga
kikoveiga previously approved these changes Apr 16, 2026
@hamorillo
hamorillo force-pushed the hector.morilloprieto/RUM-15138 branch 2 times, most recently from 4d02ee3 to 1dfbfca Compare April 21, 2026 14:01
@hamorillo
hamorillo force-pushed the hector.morilloprieto/RUM-15139 branch from 1217c89 to 5d9fa7c Compare April 23, 2026 15:18
@hamorillo
hamorillo changed the base branch from hector.morilloprieto/RUM-15138 to develop April 23, 2026 15:18
@hamorillo
hamorillo dismissed kikoveiga’s stale review April 23, 2026 15:18

The base branch was changed.

…ion Replay sampling

Also, extract SessionSamplingIdProvider to dd-sdk-android-internal
@hamorillo
hamorillo force-pushed the hector.morilloprieto/RUM-15139 branch from 5d9fa7c to f9fa86c Compare April 23, 2026 15:27
@hamorillo

Copy link
Copy Markdown
Contributor Author

@kikoveiga Thanks for your initial review.

Since #3342 is still being redesigned and discussed, I’ve removed this change’s dependencies on that code. As discussed with @0xnm, Session Replay is an independent feature with a different sampling decision point, so we can move forward with this PR.

@hamorillo
hamorillo requested a review from kikoveiga April 23, 2026 15:41
0xnm
0xnm previously approved these changes Apr 24, 2026
sdkCore.getFeature(Feature.SESSION_REPLAY_FEATURE_NAME)?.sendEvent(
mapOf(
SESSION_REPLAY_BUS_MESSAGE_TYPE_KEY to RUM_SESSION_RENEWED_BUS_MESSAGE,
RUM_KEEP_SESSION_BUS_MESSAGE_KEY to keepSession,

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.

should we keep it? I'm thinking about the setup where RUM and SR artifacts version mismatch. Unlikely it can be the case, but just to be sure that we don't break such "wrong" setups.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point! I didn't consider that case. I've added it again with a comment to remove it at V4.

private fun applySampling(alreadySeenSession: Boolean, sessionData: SessionData) {
if (!alreadySeenSession) {
isSessionSampledIn.set(rateBasedSampler.sample(Unit))
isSessionSampledIn.set(

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 wondering if we need to handle this case somehow:

https://github.com/DataDog/dd-sdk-ios/pull/2714/files#diff-7520356e0af3e6cfd9367eb32d309f0ac6aac73269bd06d88daec0d3b6e026d4R108-R111

Normally we don't expect ingestion of SR data without RUM session linked, but worth checking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I would say that on Android this case can't happen — SR only starts recording when it receives a rum_session_renewed bus event from RUM via onReceive(). Also parseSessionMetadata returns null when no RUM sessionId available so it will never reach the applySampling method.

@hamorillo
hamorillo merged commit 6bc1364 into develop Apr 24, 2026
26 checks passed
@hamorillo
hamorillo deleted the hector.morilloprieto/RUM-15139 branch April 24, 2026 11:31
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.

4 participants