Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(frame-tracking): app lag when long running span finishes #2311

Merged
merged 13 commits into from
Sep 26, 2024
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

### Fixes

-- Incoming Change
- App lag with frame tracking enabled when span finishes after a long time ([#2311](https://github.com/getsentry/sentry-dart/pull/2311))
- Only start frame tracking if we receive valid display refresh data ([#2307](https://github.com/getsentry/sentry-dart/pull/2307))
- Rounding error used on frames.total and reject frame measurements if frames.total is less than frames.slow or frames.frozen ([#2308](https://github.com/getsentry/sentry-dart/pull/2308))
- iOS replay integration when only `onErrorSampleRate` is specified ([#2306](https://github.com/getsentry/sentry-dart/pull/2306))
Expand Down
5 changes: 5 additions & 0 deletions flutter/lib/src/sentry_flutter_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ class SentryFlutterOptions extends SentryOptions {
/// Defaults to `true`
bool enableFramesTracking = true;

/// Maximum number of frames to track at a time before clearing.
/// Clearing cancels frame tracking for all active spans.
/// Default is 10800 frames, this equals to 3 minutes of frames at 60fps.
int maxFramesToTrack = 10800;
Copy link
Collaborator

Choose a reason for hiding this comment

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

l: I think a duration would be more easy to understand instead of number of frames

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the confusing part is that the callback we use from flutter doesn't actually trigger every x fps, it triggers only when new frames are drawn.

If the user doesn't do anything no scrolling etc... after 3 minutes the frames tracked might be 100 or so which doesnt align with a Duration option


/// By using this, you are disabling native [Breadcrumb] tracking and instead
/// you are just tracking [Breadcrumb]s which result from events available
/// in the current Flutter environment.
Expand Down
8 changes: 8 additions & 0 deletions flutter/lib/src/span_frame_metrics_collector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
///
/// This method is called for each frame when frame tracking is active.
Future<void> measureFrameDuration(Duration duration) async {
if (frames.length >= options.maxFramesToTrack) {
options.logger(SentryLevel.warning,
'Frame tracking limit reached. Clearing frames and cancelling frame tracking for all active spans');
clear();
return;
}

// Using the stopwatch to measure the frame duration is flaky in ci
if (_isTestMode) {
// ignore: invalid_use_of_internal_member
Expand Down Expand Up @@ -267,6 +274,7 @@ class SpanFrameMetricsCollector implements PerformanceContinuousCollector {
@override
void clear() {
_isTrackingPaused = true;
_stopwatch.reset();
frames.clear();
activeSpans.clear();
displayRefreshRate = null;
Expand Down
25 changes: 25 additions & 0 deletions flutter/test/span_frame_metrics_collector_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,31 @@ void main() {

expect(sut.isTrackingPaused, isTrue);
});

test(
'measureFrameDuration stops and removes all frames when reaching frame limit',
() async {
final sut = fixture.sut;
final span = MockSentrySpan();

when(span.startTimestamp).thenReturn(DateTime.now());
sut.activeSpans.add(span);
sut.frames[DateTime.now()] = 1;
const maxFramesToTrack = 1000;
fixture.options.maxFramesToTrack = maxFramesToTrack;

for (var i = 1; i <= maxFramesToTrack; i++) {
if (i == maxFramesToTrack - 1) {
expect(sut.frames.length, maxFramesToTrack - 1);
}
await sut.measureFrameDuration(Duration.zero);
}

expect(sut.frames, isEmpty);
expect(sut.activeSpans, isEmpty);
expect(sut.displayRefreshRate, isNull);
expect(sut.isTrackingPaused, isTrue);
});
}

class Fixture {
Expand Down
Loading