Skip to content

Commit dfd6149

Browse files
authored
Crashlytics dispatch Rollouts writes async to prevent hangs (#12977)
1 parent 61c10bc commit dfd6149

File tree

3 files changed

+43
-1
lines changed

3 files changed

+43
-1
lines changed

Crashlytics/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased
22
- [added] Added support for catching the SIGTERM signal (#12881).
3+
- [fixed] Fixed a hang when persisting Remote Config Rollouts to disk (#12913).
34

45
# 10.25.0
56
- [changed] Removed usages of user defaults API from internal Firebase Sessions

Crashlytics/Crashlytics/Controllers/FIRCLSRolloutsPersistenceManager.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,13 @@ - (void)updateRolloutsStateToPersistenceWithRollouts:(NSData *_Nonnull)rollouts
5757

5858
NSFileHandle *rolloutsFile = [NSFileHandle fileHandleForUpdatingAtPath:rolloutsPath];
5959

60-
dispatch_sync(FIRCLSGetLoggingQueue(), ^{
60+
dispatch_async(FIRCLSGetLoggingQueue(), ^{
6161
@try {
6262
[rolloutsFile seekToEndOfFile];
6363
NSMutableData *rolloutsWithNewLineData = [rollouts mutableCopy];
6464
[rolloutsWithNewLineData appendData:[@"\n" dataUsingEncoding:NSUTF8StringEncoding]];
6565
[rolloutsFile writeData:rolloutsWithNewLineData];
66+
[rolloutsFile closeFile];
6667
} @catch (NSException *exception) {
6768
FIRCLSDebugLog(@"Failed to write new rollouts. Exception name: %s - message: %s",
6869
exception.name, exception.reason);

Crashlytics/UnitTests/FIRCLSRolloutsPersistenceManagerTests.m

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#import <XCTest/XCTest.h>
1717

1818
#import "Crashlytics/Crashlytics/Components/FIRCLSContext.h"
19+
#include "Crashlytics/Crashlytics/Components/FIRCLSGlobals.h"
1920
#import "Crashlytics/Crashlytics/Controllers/FIRCLSRolloutsPersistenceManager.h"
2021
#import "Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h"
2122
#import "Crashlytics/UnitTests/Mocks/FIRCLSTempMockFileManager.h"
@@ -51,6 +52,9 @@ - (void)tearDown {
5152
}
5253

5354
- (void)testUpdateRolloutsStateToPersistenceWithRollouts {
55+
XCTestExpectation *expectation = [[XCTestExpectation alloc]
56+
initWithDescription:@"Expect updating rollouts to finish writing."];
57+
5458
NSString *encodedStateString =
5559
@"{rollouts:[{\"parameter_key\":\"6d795f66656174757265\",\"parameter_value\":"
5660
@"\"e8bf99e698af7468656d6973e79a84e6b58be8af95e695b0e68daeefbc8ce8be93e585a5e4b8ade69687\","
@@ -64,7 +68,43 @@ - (void)testUpdateRolloutsStateToPersistenceWithRollouts {
6468

6569
[self.rolloutsPersistenceManager updateRolloutsStateToPersistenceWithRollouts:data
6670
reportID:reportId];
71+
72+
// Wait for the logging queue to finish.
73+
dispatch_async(FIRCLSGetLoggingQueue(), ^{
74+
[expectation fulfill];
75+
});
76+
77+
[self waitForExpectations:@[ expectation ] timeout:3];
78+
6779
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:rolloutsFilePath]);
6880
}
6981

82+
- (void)testUpdateRolloutsStateToPersistenceEnsureNoHang {
83+
dispatch_queue_t testQueue = dispatch_queue_create("TestQueue", DISPATCH_QUEUE_SERIAL);
84+
XCTestExpectation *expectation =
85+
[[XCTestExpectation alloc] initWithDescription:@"Expect updating rollouts to return."];
86+
NSString *encodedStateString =
87+
@"{rollouts:[{\"parameter_key\":\"6d795f66656174757265\",\"parameter_value\":"
88+
@"\"e8bf99e698af7468656d6973e79a84e6b58be8af95e695b0e68daeefbc8ce8be93e585a5e4b8ade69687\","
89+
@"\"rollout_id\":\"726f6c6c6f75745f31\",\"template_version\":1,\"variant_id\":"
90+
@"\"636f6e74726f6c\"}]}";
91+
92+
NSData *data = [encodedStateString dataUsingEncoding:NSUTF8StringEncoding];
93+
94+
// Clog up the queue with a long running operation. This sleep time
95+
// must be longer than the expectation timeout.
96+
dispatch_async(FIRCLSGetLoggingQueue(), ^{
97+
sleep(10);
98+
});
99+
100+
dispatch_async(testQueue, ^{
101+
// Ensure that calling this returns quickly so we don't hang
102+
[self.rolloutsPersistenceManager updateRolloutsStateToPersistenceWithRollouts:data
103+
reportID:reportId];
104+
[expectation fulfill];
105+
});
106+
107+
[self waitForExpectations:@[ expectation ] timeout:3];
108+
}
109+
70110
@end

0 commit comments

Comments
 (0)