Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
676b276
fix(session-replay): change multi-threading of session replay processing
philprime Mar 25, 2025
2fda9c4
small fixes
philprime Mar 25, 2025
aa26915
improvements
philprime Mar 25, 2025
a27d74d
WIP
philprime Mar 25, 2025
23c6556
Merge remote-tracking branch 'origin/main' into philprime/session-rep…
philprime Apr 11, 2025
8634126
wip
philprime Apr 11, 2025
3ff7c84
add changelog
philprime Apr 11, 2025
a3d948b
Merge remote-tracking branch 'origin/main' into philprime/session-rep…
philprime Apr 11, 2025
44c4af7
Apply suggestions from code review
philprime Apr 11, 2025
c0e828d
Merge remote-tracking branch 'origin/main' into philprime/session-rep…
philprime Apr 24, 2025
773fcad
reverting changes
philprime Apr 24, 2025
0e9c3e2
small changes
philprime Apr 24, 2025
c4ba77c
WIP
philprime Apr 24, 2025
c5c78ca
Merge remote-tracking branch 'origin/main' into philprime/session-rep…
philprime Apr 25, 2025
ef3d279
fix merge error
philprime Apr 25, 2025
51beda1
small fixes
philprime Apr 25, 2025
1787110
Merge remote-tracking branch 'origin/main' into philprime/session-rep…
philprime Apr 25, 2025
8614798
small reverts
philprime Apr 25, 2025
bdfd4bc
small reverts
philprime Apr 25, 2025
e63bbeb
Merge remote-tracking branch 'origin/main' into philprime/session-rep…
philprime Apr 30, 2025
0cfab38
fix sync filtering
philprime Apr 30, 2025
015e2ee
Added more logs and tests; moved types to own files due to linter war…
philprime Apr 30, 2025
e1ef89d
removed unused type
philprime Apr 30, 2025
d723f27
fix tests
philprime Apr 30, 2025
168bb3d
Merge remote-tracking branch 'origin/main' into philprime/session-rep…
philprime Apr 30, 2025
c9e005b
Merge remote-tracking branch 'origin/main' into philprime/session-rep…
philprime May 15, 2025
01ea965
update changelog
philprime May 15, 2025
fd16c22
WIP
philprime May 15, 2025
a065663
add dispatch queue provider
philprime May 16, 2025
52b5290
Merge remote-tracking branch 'origin/main' into philprime/session-rep…
philprime May 16, 2025
93479d0
revert outdated changes
philprime May 16, 2025
ffb04e1
fixes and reverts
philprime May 16, 2025
58ea599
remove unnecessary error propagation
philprime May 16, 2025
eb47b7f
Merge remote-tracking branch 'origin/main' into philprime/session-rep…
philprime May 19, 2025
073fab8
update docs and changelog
philprime May 19, 2025
fa9a3b7
fix merge conflict in changelog
philprime May 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Crash in setMeasurement when name is nil (#5064)
- Make setMeasurement thread safe (#5067, #5078)
- Fix thread inversion warning in session replay (#5018)

## 8.49.0

Expand Down
4 changes: 4 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,7 @@
D4AF00212D2E92FD00F5F3D7 /* SentryNSFileManagerSwizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = D4AF00202D2E92FD00F5F3D7 /* SentryNSFileManagerSwizzling.m */; };
D4AF00232D2E931000F5F3D7 /* SentryNSFileManagerSwizzling.h in Headers */ = {isa = PBXBuildFile; fileRef = D4AF00222D2E931000F5F3D7 /* SentryNSFileManagerSwizzling.h */; };
D4AF00252D2E93C400F5F3D7 /* SentryNSFileManagerSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D4AF00242D2E93C400F5F3D7 /* SentryNSFileManagerSwizzlingTests.m */; };
D4B0DC7F2DA9257A00DE61B6 /* SentryRenderVideoResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4B0DC7E2DA9257200DE61B6 /* SentryRenderVideoResult.swift */; };
D4C5F59A2D4249E6002A9BF6 /* DataSentryTracingIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C5F5992D4249E0002A9BF6 /* DataSentryTracingIntegrationTests.swift */; };
D4E3F35D2D4A864600F79E2B /* SentryNSDictionarySanitizeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D42E48582D48FC8F00D251BC /* SentryNSDictionarySanitizeTests.swift */; };
D4E3F35E2D4A877300F79E2B /* SentryNSDictionarySanitize+Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = D41909942D490006002B83D0 /* SentryNSDictionarySanitize+Tests.m */; };
Expand Down Expand Up @@ -2014,6 +2015,7 @@
D4AF00202D2E92FD00F5F3D7 /* SentryNSFileManagerSwizzling.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryNSFileManagerSwizzling.m; sourceTree = "<group>"; };
D4AF00222D2E931000F5F3D7 /* SentryNSFileManagerSwizzling.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryNSFileManagerSwizzling.h; path = include/SentryNSFileManagerSwizzling.h; sourceTree = "<group>"; };
D4AF00242D2E93C400F5F3D7 /* SentryNSFileManagerSwizzlingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryNSFileManagerSwizzlingTests.m; sourceTree = "<group>"; };
D4B0DC7E2DA9257200DE61B6 /* SentryRenderVideoResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryRenderVideoResult.swift; sourceTree = "<group>"; };
D4C5F5992D4249E0002A9BF6 /* DataSentryTracingIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSentryTracingIntegrationTests.swift; sourceTree = "<group>"; };
D4E829D12D75E2DE00D375AD /* SentryDefaultViewRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryDefaultViewRenderer.swift; sourceTree = "<group>"; };
D4E829D32D75E34A00D375AD /* SentryExperimentalViewRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryExperimentalViewRenderer.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4269,6 +4271,7 @@
D8CAC02A2BA0663E00E38F34 /* SentryReplayOptions.swift */,
D8CAC02B2BA0663E00E38F34 /* SentryVideoInfo.swift */,
D802994D2BA836EF000F0081 /* SentryOnDemandReplay.swift */,
D4B0DC7E2DA9257200DE61B6 /* SentryRenderVideoResult.swift */,
D451ED5E2D92ECDE00C9BEA8 /* SentryReplayFrame.swift */,
D451ED5C2D92ECD200C9BEA8 /* SentryOnDemandReplayError.swift */,
D802994F2BA83A88000F0081 /* SentryPixelBuffer.swift */,
Expand Down Expand Up @@ -4959,6 +4962,7 @@
7B98D7D325FB65AE00C5A389 /* SentryWatchdogTerminationTracker.m in Sources */,
8E564AE8267AF22600FE117D /* SentryNetworkTrackingIntegration.m in Sources */,
63AA75EF1EB8B3C400D153DE /* SentryClient.m in Sources */,
D4B0DC7F2DA9257A00DE61B6 /* SentryRenderVideoResult.swift in Sources */,
7B7D873624864C9D00D2ECFF /* SentryCrashDefaultMachineContextWrapper.m in Sources */,
63FE712F20DA4C1100CDBAE8 /* SentryCrashSysCtl.c in Sources */,
62212B872D520CB00062C2FA /* SentryEventCodable.swift in Sources */,
Expand Down
8 changes: 8 additions & 0 deletions Sources/Sentry/SentryDispatchQueueWrapper.m
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ - (nullable dispatch_block_t)createDispatchBlock:(void (^)(void))block
return dispatch_block_create(0, block);
}

+ (SentryDispatchQueueWrapper *)createBackgroundDispatchQueueWithName:(const char *)name
relativePriority:(int)relativePriority

{
dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(
DISPATCH_QUEUE_SERIAL, QOS_CLASS_BACKGROUND, relativePriority);
return [[SentryDispatchQueueWrapper alloc] initWithName:name attributes:attributes];
}
@end

NS_ASSUME_NONNULL_END
84 changes: 60 additions & 24 deletions Sources/Sentry/SentrySessionReplayIntegration.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ @implementation SentrySessionReplayIntegration {
// replay absolutely needs segment 0 to make replay work.
BOOL _rateLimited;
id<SentryCurrentDateProvider> _dateProvider;
SentryDispatchQueueWrapper *_replayProcessingQueue;
SentryDispatchQueueWrapper *_replayAssetWorkerQueue;
}

- (instancetype)init
Expand Down Expand Up @@ -122,6 +124,19 @@ - (void)setupWith:(SentryReplayOptions *)replayOptions
}

_notificationCenter = SentryDependencyContainer.sharedInstance.notificationCenterWrapper;
_dateProvider = SentryDependencyContainer.sharedInstance.dateProvider;

// The asset worker queue is used to work on video and frames data.
// Use a relative priority of -1 to make it lower than the default background priority.
_replayAssetWorkerQueue = [SentryDispatchQueueWrapper
createBackgroundDispatchQueueWithName:"io.sentry.session-replay.asset-worker"
relativePriority:-1];
// The dispatch queue is used to asynchronously wait for the asset worker queue to finish its
// work. To avoid a deadlock, the priority of the processing queue must be lower than the asset
// worker queue. Use a relative priority of -2 to make it lower than the asset worker queue.
_replayProcessingQueue = [SentryDispatchQueueWrapper
createBackgroundDispatchQueueWithName:"io.sentry.session-replay.processing"
relativePriority:-2];

// The asset worker queue is used to work on video and frames data.

Expand Down Expand Up @@ -193,7 +208,10 @@ - (void)resumePreviousSessionReplay:(SentryEvent *)event
}

SentryOnDemandReplay *resumeReplayMaker =
[[SentryOnDemandReplay alloc] initWithContentFrom:lastReplayURL.path];
[[SentryOnDemandReplay alloc] initWithContentFrom:lastReplayURL.path
processingQueue:_replayProcessingQueue
assetWorkerQueue:_replayAssetWorkerQueue
dateProvider:_dateProvider];
resumeReplayMaker.bitRate = _replayOptions.replayBitRate;
resumeReplayMaker.videoScale = _replayOptions.sizeScale;

Expand All @@ -205,14 +223,35 @@ - (void)resumePreviousSessionReplay:(SentryEvent *)event
}
NSDate *end = [beginning dateByAddingTimeInterval:duration];

NSError *error;
NSArray<SentryVideoInfo *> *videos = [resumeReplayMaker createVideoWithBeginning:beginning
end:end
error:&error];
if (videos == nil) {
// This method is called from a background thread, so we can synchronize the creation of the
// video with a dispatch group.
__block NSArray<SentryVideoInfo *> *videos;
__block NSError *_Nullable error;

dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[resumeReplayMaker
createVideoAsyncWithBeginning:beginning
end:end
completion:^(NSArray<SentryVideoInfo *> *_Nullable resultVideos,
NSError *_Nullable resultError) {
videos = resultVideos;
error = resultError;
dispatch_group_leave(group);
}];
// Wait for the video creation to finish without a timeout, because the video creation is
// expected to finish in a reasonable time frame.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

// Either error or videos should be set.
if (error != nil) {
SENTRY_LOG_ERROR(@"Could not create replay video, reason: %@", error);
return;
}
if (videos == nil) {
SENTRY_LOG_ERROR(@"Could not create replay video, reason: no videos available");
return;
}

// For each segment we need to create a new event with the video.
int _segmentId = segmentId;
Expand Down Expand Up @@ -323,30 +362,27 @@ - (void)startWithOptions:(SentryReplayOptions *)replayOptions
error:nil];
}

SentryOnDemandReplay *replayMaker = [[SentryOnDemandReplay alloc] initWithOutputPath:docs.path];
SentryOnDemandReplay *replayMaker =
[[SentryOnDemandReplay alloc] initWithOutputPath:docs.path
processingQueue:_replayProcessingQueue
assetWorkerQueue:_replayAssetWorkerQueue
dateProvider:_dateProvider];
replayMaker.bitRate = replayOptions.replayBitRate;
replayMaker.videoScale = replayOptions.sizeScale;
replayMaker.cacheMaxSize
= (NSInteger)(shouldReplayFullSession ? replayOptions.sessionSegmentDuration + 1
: replayOptions.errorReplayDuration + 1);

dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(
DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_PRIORITY_LOW, 0);
SentryDispatchQueueWrapper *dispatchQueue =
[[SentryDispatchQueueWrapper alloc] initWithName:"io.sentry.session-replay"
attributes:attributes];

self.sessionReplay = [[SentrySessionReplay alloc]
initWithReplayOptions:replayOptions
replayFolderPath:docs
screenshotProvider:screenshotProvider
replayMaker:replayMaker
breadcrumbConverter:breadcrumbConverter
touchTracker:_touchTracker
dateProvider:SentryDependencyContainer.sharedInstance.dateProvider
delegate:self
dispatchQueue:dispatchQueue
displayLinkWrapper:[[SentryDisplayLinkWrapper alloc] init]];
SentryDisplayLinkWrapper *displayLinkWrapper = [[SentryDisplayLinkWrapper alloc] init];
self.sessionReplay = [[SentrySessionReplay alloc] initWithReplayOptions:replayOptions
replayFolderPath:docs
screenshotProvider:screenshotProvider
replayMaker:replayMaker
breadcrumbConverter:breadcrumbConverter
touchTracker:_touchTracker
dateProvider:_dateProvider
delegate:self
displayLinkWrapper:displayLinkWrapper];

[self.sessionReplay
startWithRootView:SentryDependencyContainer.sharedInstance.application.windows.firstObject
Expand Down
2 changes: 2 additions & 0 deletions Sources/Sentry/include/SentryDispatchQueueWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ NS_ASSUME_NONNULL_BEGIN

- (nullable dispatch_block_t)createDispatchBlock:(void (^)(void))block;

+ (SentryDispatchQueueWrapper *)createBackgroundDispatchQueueWithName:(const char *)name
relativePriority:(int)relativePriority;
@end

NS_ASSUME_NONNULL_END
Loading
Loading