Skip to content

fix(tracing): Fix native frames measurements dropped for idle transactions#5813

Open
antonis wants to merge 3 commits intomainfrom
antonis/idle-native-frames
Open

fix(tracing): Fix native frames measurements dropped for idle transactions#5813
antonis wants to merge 3 commits intomainfrom
antonis/idle-native-frames

Conversation

@antonis
Copy link
Contributor

@antonis antonis commented Mar 13, 2026

📢 Type of change

  • Bugfix

📜 Description

The NativeFrames integration was silently dropping transaction-level frame measurements (frames_total, frames_slow, frames_frozen) for idle transactions with the warning:

[NativeFrames] Frames were collected within larger than margin of error delay for spanId. Dropping the inaccurate values.

This affected all idle transactions (navigation-based performance monitoring), causing slow/frozen frame metrics to be missing from the Performance pages.

Three issues fixed in the child span end frames fallback path of processEvent:

  1. Race condition: Child span end frames were stored in the map only after two async awaits completed (_spanToNativeFramesAtStartMap.get() and fetchNativeFrames()). If processEvent ran before those awaits finished, the fallback data was unavailable. Now stores a Promise immediately before any awaits, matching the pattern used by the root span end frames path.

  2. Timestamp mismatch: Used timestampInSeconds() (wall-clock time) for the child span end timestamp, but compared it against event.timestamp which is the span's actual end time. For idle transactions where event.timestamp is trimmed to the last child span's end time, the wall-clock time at spanEnd event firing could differ. Now uses spanToJSON(span).timestamp — the same timestamp source the idle proxy trims to.

  3. Concurrent transaction interference: The fallback was a single global variable (_lastChildSpanEndFrames) overwritten by any transaction's child spans. Now scoped per root span using AsyncExpiringMap keyed by root span ID, with a 60s TTL to survive idle timeouts + processing delays.

Bonus: child spans now reuse the same fetchNativeFrames() promise for both the fallback map and span attributes, eliminating a redundant native bridge call per child span.

💡 Motivation and Context

Reported by a customer on SDK v6.15.1 seeing 100% of idle transactions missing slow/frozen frame measurements. The issue exists on both iOS and Android and affects all SDK versions (v6 and v7/v8) since the processEvent fallback logic has not changed.

Related: #4723

💚 How did you test it?

  • Added test: falls back to last child span end frames when root span end timestamp does not match event timestamp (idle transaction trim) — simulates idle transaction trimming by shifting event.timestamp back to child span end time and advancing mock time for root span end, verifying the fallback path produces correct measurements.
  • All existing NativeFrames tests pass (10/10).
  • Build compiles successfully.

📝 Checklist

  • I added tests to verify changes
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • All tests passing
  • No breaking changes

🔮 Next steps

…tions

The NativeFrames integration was dropping transaction-level frame measurements
(frames_total, frames_slow, frames_frozen) for all idle transactions because
the fallback child span end frames were either unavailable or had mismatched
timestamps when processEvent ran.

Three issues fixed:
- Race condition: child span end frames stored after async awaits, so
  processEvent could run before data was available. Now stores a Promise
  immediately before any awaits.
- Timestamp mismatch: used wall-clock time (timestampInSeconds) instead of
  actual span timestamp (spanToJSON), causing isClose check to fail when
  comparing against the trimmed event.timestamp.
- Concurrent transaction interference: single global variable overwritten by
  any transaction's child spans. Now scoped per root span using AsyncExpiringMap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

github-actions bot commented Mar 13, 2026

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


  • fix(tracing): Fix native frames measurements dropped for idle transactions by antonis in #5813
  • Ref: remove yarn from stub update by lucas-zimerman in #5811
  • Ref(CI): Unify stub update with android update by lucas-zimerman in #5807

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 13, 2026

Fails
🚫 Pull request is not ready for merge, please add the "ready-to-merge" label to the pull request

Generated by 🚫 dangerJS against ff103c3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@antonis
Copy link
Contributor Author

antonis commented Mar 13, 2026

@sentry review

@antonis
Copy link
Contributor Author

antonis commented Mar 13, 2026

@claude review

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@antonis antonis marked this pull request as ready for review March 13, 2026 14:53
childEndFramesPromise
.then(frames => ({ timestamp: spanTimestamp, nativeFrames: frames }))
.then(undefined, error => {
debug.log(`[${INTEGRATION_NAME}] Error while fetching child span end frames.`, error);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Won't this and line 195 generated duplicated error logs?

  1. Line 138: "Error while fetching child span end frames."                                                   
  2. Line 195: "Error while capturing end frames for span ${spanId}."                                          

@lucas-zimerman
Copy link
Collaborator

Q: Concurrent transaction interference is there a test to validate this case?

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