Skip to content

feat(replay): Add traces_by_timestamp to replay event#18048

Open
billyvg wants to merge 12 commits intodevelopfrom
billy/replay-802-replay-sdk-needs-to-send-timestamps-w-trace-ids
Open

feat(replay): Add traces_by_timestamp to replay event#18048
billyvg wants to merge 12 commits intodevelopfrom
billy/replay-802-replay-sdk-needs-to-send-timestamps-w-trace-ids

Conversation

@billyvg
Copy link
Member

@billyvg billyvg commented Oct 28, 2025

In order to support moving our custom rrweb events to EAP, we need to send timestamps w/ trace ids so that we can identify which trace the event belongs to.

In order to avoid breaking changes, we should not change the current type of trace_ids field in the replay event, instead we add a new field traces_by_timestamp w/ type [transaction.start_timestamp, traceId][].

Previously, we would clear all trace ids after each replay segment. so if there is not a new transaction, segments would not have a trace id associated at all. I've changed this so that we always keep the most recent trace id in between segments.

Also previously, in order to associate a replay with a trace, we wait until after a type: transaction event is sent successfully. it's possible that the replay integration is loaded after this event is sent and never gets associated with any trace ids. in this case, I've changed it so that we take the trace id from current scope and propagation context, and I use -1 for the timestamp here, but could also change to use current ts.

Closes #19201 (added automatically)

@linear
Copy link

linear bot commented Oct 28, 2025

@github-actions
Copy link
Contributor

github-actions bot commented Oct 28, 2025

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 25.62 kB - -
@sentry/browser - with treeshaking flags 24.12 kB - -
@sentry/browser (incl. Tracing) 42.42 kB - -
@sentry/browser (incl. Tracing, Profiling) 47.09 kB - -
@sentry/browser (incl. Tracing, Replay) 81.33 kB +0.11% +86 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 70.92 kB +0.09% +62 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 86.02 kB +0.1% +79 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 98.28 kB +0.08% +78 B 🔺
@sentry/browser (incl. Feedback) 42.43 kB - -
@sentry/browser (incl. sendFeedback) 30.29 kB - -
@sentry/browser (incl. FeedbackAsync) 35.34 kB - -
@sentry/browser (incl. Metrics) 26.79 kB - -
@sentry/browser (incl. Logs) 26.93 kB - -
@sentry/browser (incl. Metrics & Logs) 27.61 kB - -
@sentry/react 27.37 kB - -
@sentry/react (incl. Tracing) 44.76 kB - -
@sentry/vue 30.07 kB - -
@sentry/vue (incl. Tracing) 44.27 kB - -
@sentry/svelte 25.64 kB - -
CDN Bundle 28.16 kB - -
CDN Bundle (incl. Tracing) 43.25 kB - -
CDN Bundle (incl. Logs, Metrics) 29 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 44.09 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 68.16 kB +0.12% +76 B 🔺
CDN Bundle (incl. Tracing, Replay) 80.2 kB +0.09% +70 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 81.05 kB +0.08% +59 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 85.71 kB +0.08% +66 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 86.59 kB +0.08% +61 B 🔺
CDN Bundle - uncompressed 82.34 kB - -
CDN Bundle (incl. Tracing) - uncompressed 128.06 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 85.18 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 130.89 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 209.13 kB +0.14% +288 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 245.23 kB +0.12% +288 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 248.05 kB +0.12% +288 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 258.14 kB +0.12% +288 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 260.95 kB +0.12% +288 B 🔺
@sentry/nextjs (client) 47.17 kB - -
@sentry/sveltekit (client) 42.88 kB - -
@sentry/node-core 52.18 kB +0.02% +9 B 🔺
@sentry/node 174.48 kB +0.01% +4 B 🔺
@sentry/node - without tracing 97.33 kB +0.01% +8 B 🔺
@sentry/aws-serverless 113.13 kB +0.01% +8 B 🔺

View base workflow run

@github-actions
Copy link
Contributor

github-actions bot commented Oct 28, 2025

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.
⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 8,650 - 8,912 -3%
GET With Sentry 1,681 19% 1,675 +0%
GET With Sentry (error only) 6,038 70% 6,038 -
POST Baseline 1,180 - 1,205 -2%
POST With Sentry 586 50% 595 -2%
POST With Sentry (error only) 1,041 88% 1,052 -1%
MYSQL Baseline 3,208 - 3,293 -3%
MYSQL With Sentry 529 16% 478 +11%
MYSQL With Sentry (error only) 2,624 82% 2,668 -2%

View base workflow run

@billyvg
Copy link
Member Author

billyvg commented Oct 28, 2025

@sentry review

@billyvg
Copy link
Member Author

billyvg commented Oct 29, 2025

@sentry review

@billyvg billyvg marked this pull request as ready for review October 29, 2025 22:24
@billyvg billyvg requested a review from a team as a code owner October 29, 2025 22:24
@billyvg billyvg requested a review from chargome October 29, 2025 22:24
if (this._context.traceIds.length === 0) {
const currentTraceId = getCurrentScope().getPropagationContext().traceId;
if (currentTraceId) {
this._context.traceIds.push([-1, currentTraceId]);
Copy link
Member

Choose a reason for hiding this comment

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

Could we put a comment in here explaining the -1 ?

@billyvg billyvg force-pushed the billy/replay-802-replay-sdk-needs-to-send-timestamps-w-trace-ids branch from 1985467 to 84b12da Compare February 5, 2026 21:38
@github-actions
Copy link
Contributor

github-actions bot commented Feb 5, 2026

Codecov Results 📊


Generated by Codecov Action

In order to support moving our custom rrweb events to EAP, we need to send timestamps w/ trace ids so that we can identify which trace the event belongs to.

In order to avoid breaking changes, we should not change the current type of trace_ids field in the replay event, instead we add a new field traces_by_timestamp
@andreiborza andreiborza force-pushed the billy/replay-802-replay-sdk-needs-to-send-timestamps-w-trace-ids branch from 84b12da to f50a1cb Compare February 18, 2026 20:25
andreiborza added a commit that referenced this pull request Feb 24, 2026
This is a temporary branch to cut a beta release for #18048.
initialUrl: this._context.initialUrl,
errorIds: Array.from(this._context.errorIds),
traceIds: Array.from(this._context.traceIds),
traceIds: this._context.traceIds,
Copy link

Choose a reason for hiding this comment

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

Bug: A race condition exists because _popEventContext returns a direct reference to the traceIds array, allowing it to be mutated during the async sendReplay operation.
Severity: MEDIUM

Suggested Fix

In _popEventContext, return a shallow copy of the traceIds array instead of a direct reference. This can be achieved by using the spread syntax [...this._context.traceIds] or the slice() method.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: packages/replay-internal/src/replay.ts#L1159

Potential issue: The `_popEventContext` method returns a context object containing a
direct reference to the internal `_context.traceIds` array, not a copy. When a segment
flush is triggered with one or zero trace IDs, the subsequent call to `_clearContext`
does not reassign this array. This creates a race condition: during the `await` for the
asynchronous `sendReplay` operation, a new transaction event can trigger
`handleAfterSendEvent`, which pushes a new trace ID onto the shared array. As a result,
the replay segment being sent is mutated to include a trace ID that was captured after
the segment was finalized, leading to incorrect trace linkage.

Did we get this right? 👍 / 👎 to inform future reviews.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable autofix in the Cursor dashboard.

initialUrl: this._context.initialUrl,
errorIds: Array.from(this._context.errorIds),
traceIds: Array.from(this._context.traceIds),
traceIds: this._context.traceIds,
Copy link

Choose a reason for hiding this comment

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

Shared mutable array reference causes race condition during flush

Medium Severity

In _popEventContext, traceIds is assigned as a direct reference (traceIds: this._context.traceIds) instead of a copy. The old code used Array.from(this._context.traceIds). When _clearContext runs and traceIds.length <= 1, the array is not reassigned — so the returned context and this._context share the same mutable array. In _runFlush, there's an await this.eventBuffer.finish() between _popEventContext() and sendReplay(), during which new transaction events can .push() into the shared array, corrupting the data being sent.

Additional Locations (1)

Fix in Cursor Fix in Web

billyvg added a commit to getsentry/sentry that referenced this pull request Feb 26, 2026
Upgrades Sentry SDKs to 10.41.0-beta.0 to test out the new traces by timestamp for replay events for EAP trace association. getsentry/sentry-javascript#18048
billyvg added a commit to getsentry/sentry that referenced this pull request Feb 26, 2026
Upgrades Sentry SDKs to 10.41.0-beta.0 to test out the new traces by
timestamp for replay events for EAP trace association.
getsentry/sentry-javascript#18048
billyvg added a commit to getsentry/sentry-docs that referenced this pull request Feb 26, 2026
Document the new `traces_by_timestamp` field added in
getsentry/sentry-javascript#18048. This field provides timestamped
trace associations as `[timestamp, trace_id]` pairs, enabling Sentry
to correlate replay events with the correct trace by temporal ordering.

The existing `trace_ids` field is preserved for backwards compatibility.

Co-Authored-By: Claude <noreply@anthropic.com>
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.

feat(replay): Add traces_by_timestamp to replay event

3 participants