Skip to content

perf(android): Replace RNSentryFrameDelayCollector with queryable sentry-java API #5908

Description

@antonis

Context

In #5907, we added frames.delay span data by creating RNSentryFrameDelayCollector — a listener-based collector that registers with sentry-java's SentryFrameMetricsCollector to accumulate per-frame delay data. This works, but it duplicates work already done internally by sentry-java's SpanFrameMetricsCollector, adding unnecessary overhead:

  • Double listener callbacks: Every frame triggers two listener callbacks on the UI thread (one for sentry-java's internal collector, one for ours)
  • Double storage: Two separate data structures holding the same per-frame delay records
  • Redundant computation: Both collectors compute delay overlap with time ranges independently

Proposed Solution

Add a queryable API to sentry-java (on SentryFrameMetricsCollector or a new utility), mirroring iOS's existing SentryFramesTracker.getFramesDelaySPI:

// New method on SentryFrameMetricsCollector (or SentryAndroidOptions accessor)
public SentryFramesDelayResult getFramesDelay(long startSystemNanos, long endSystemNanos);
public class SentryFramesDelayResult {
    public final double delayDuration;  // in seconds, -1 if incalculable
    public final int framesContributingToDelayCount;
}

iOS Reference

sentry-cocoa already exposes this via SentryFramesTracker.getFramesDelaySPI(_:endSystemTimestamp:) which returns SentryFramesDelayResultSPI with delayDuration and framesContributingToDelayCount. The RN SDK calls this directly without any custom collector.

Implementation Plan

Step 1: sentry-java changes

  1. Add getFramesDelay(startNanos, endNanos) to SentryFrameMetricsCollector (or expose it on SpanFrameMetricsCollector)

    • Internally, SpanFrameMetricsCollector already stores frame records and computes delay via captureFrameMetrics() — extract the delay calculation into a standalone query method
    • Accept System.nanoTime()-based timestamps
    • Handle partial frame overlap at query boundaries (same logic already exists in SpanFrameMetricsCollector.captureFrameMetrics())
    • Return delay in seconds or -1 if incalculable
  2. Add SentryFramesDelayResult data class

  3. Expose through SentryAndroidOptions.getFrameMetricsCollector() (already exists)

Step 2: sentry-react-native changes

  1. Delete RNSentryFrameDelayCollector.java entirely
  2. Simplify RNSentryModuleImpl.fetchNativeFramesDelay():
    public void fetchNativeFramesDelay(double startTimestampSeconds, double endTimestampSeconds, Promise promise) {
        SentryOptions options = Sentry.getCurrentScopes().getOptions();
        if (!(options instanceof SentryAndroidOptions)) {
            promise.resolve(null);
            return;
        }
        SentryFrameMetricsCollector collector = ((SentryAndroidOptions) options).getFrameMetricsCollector();
        // ... timestamp conversion ...
        SentryFramesDelayResult result = collector.getFramesDelay(startNanos, endNanos);
        promise.resolve(result != null && result.delayDuration >= 0 ? result.delayDuration : null);
    }
  3. Remove listener start/stop from enableNativeFramesTracking() / disableNativeFramesTracking()

Benefits

  • Zero additional per-frame overhead (no duplicate listener)
  • No duplicate data storage
  • Simpler RN SDK code (delete ~130 lines, replace with ~5 lines)
  • Consistent API shape across iOS and Android

Blocked on

  • sentry-java exposing a queryable frames delay API (needs a sentry-java issue/PR)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions