Skip to content

Commit

Permalink
Fix: Dont create transaction for unused ViewControllers
Browse files Browse the repository at this point in the history
  • Loading branch information
brustolin committed Oct 16, 2024
1 parent 149877e commit 69d88a2
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 1 deletion.
14 changes: 13 additions & 1 deletion Sources/Sentry/SentryTracer.m
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ @implementation SentryTracer {
dispatch_block_t _idleTimeoutBlock;
NSMutableArray<id<SentrySpan>> *_children;
BOOL _startTimeChanged;
BOOL _timeout;
NSObject *_idleTimeoutLock;

#if SENTRY_HAS_UIKIT
Expand Down Expand Up @@ -148,6 +149,8 @@ - (instancetype)initWithTransactionContext:(SentryTransactionContext *)transacti
self.wasFinishCalled = NO;
_measurements = [[NSMutableDictionary alloc] init];
self.finishStatus = kSentrySpanStatusUndefined;
self.finishMustBeCalled = NO;
_timeout = YES;

if (_configuration.timerFactory == nil) {
_configuration.timerFactory = [[SentryNSTimerFactory alloc] init];
Expand Down Expand Up @@ -289,6 +292,8 @@ - (void)startDeadlineTimer
- (void)deadlineTimerFired
{
SENTRY_LOG_DEBUG(@"Sentry tracer deadline fired");
_timeout = YES;

@synchronized(self) {
// This try to minimize a race condition with a proper call to `finishInternal`.
if (self.isFinished) {
Expand All @@ -303,7 +308,8 @@ - (void)deadlineTimerFired
}
}

[self finishWithStatus:kSentrySpanStatusDeadlineExceeded];
_finishStatus = kSentrySpanStatusDeadlineExceeded;
[self finishInternal];
}

- (void)cancelDeadlineTimer
Expand Down Expand Up @@ -581,6 +587,12 @@ - (void)finishInternal
}
}];

if (self.finishMustBeCalled && !self.wasFinishCalled) {
SENTRY_LOG_DEBUG(
@"Not capturing transaction because finish was not called before timing out.");
return;
}

@synchronized(_children) {
if (_configuration.idleTimeout > 0.0 && _children.count == 0) {
SENTRY_LOG_DEBUG(@"Was waiting for timeout for UI event trace but it had no children, "
Expand Down
6 changes: 6 additions & 0 deletions Sources/Sentry/SentryUIViewControllerPerformanceTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ - (void)createTransaction:(UIViewController *)controller
SENTRY_LOG_DEBUG(@"Started span with id %@ to track view controller %@.",
spanId.sentrySpanIdString, name);

id<SentrySpan> vcSpan = [self.tracker getSpan:spanId];
if ([vcSpan isKindOfClass:SentryTracer.class]) {
SentryTracer *vcTracer = (SentryTracer *)vcSpan;
vcTracer.finishMustBeCalled = YES;
}

// Use the target itself to store the spanId to avoid using a global mapper.
objc_setAssociatedObject(controller, &SENTRY_UI_PERFORMANCE_TRACKER_SPAN_ID, spanId,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
Expand Down
7 changes: 7 additions & 0 deletions Sources/Sentry/include/SentryTracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ static const NSTimeInterval SENTRY_AUTO_TRANSACTION_MAX_DURATION = 500.0;

@property (nullable, nonatomic, copy) BOOL (^shouldIgnoreWaitForChildrenCallback)(id<SentrySpan>);

/**
* This flag indicates whether the trace should be captured when the timeout triggers.
* If Yes, this tracer will be discarced in case the timeout triggers.
* Default @c NO
*/
@property (nonatomic) BOOL finishMustBeCalled;

/**
* All the spans that where created with this tracer but rootSpan.
*/
Expand Down
9 changes: 9 additions & 0 deletions Tests/SentryTests/Transaction/SentryTracerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,15 @@ class SentryTracerTests: XCTestCase {
}
#endif

func testFinishShouldBeCalled_Timeout_NotCaptured() {
fixture.dispatchQueue.blockBeforeMainBlock = { true }

let sut = fixture.getSut()
sut.finishMustBeCalled = true
fixture.timerFactory.fire()
assertTransactionNotCaptured(sut)
}

@available(*, deprecated)
func testSetExtra_ForwardsToSetData() {
let sut = fixture.getSut()
Expand Down

0 comments on commit 69d88a2

Please sign in to comment.