Skip to content

Commit db42bae

Browse files
authored
move more jobs to disk write (#6156)
Move file clean up task to disk write worker. Fix unit test: close session has to run on a background thread
1 parent 9017c35 commit db42bae

File tree

5 files changed

+57
-26
lines changed

5 files changed

+57
-26
lines changed

firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/common/CrashlyticsControllerTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,10 @@ public void testWriteNonFatal_callsSessionReportingCoordinatorPersistNonFatal()
213213
.thenReturn(new TreeSet<>(Collections.singleton(sessionId)));
214214

215215
controller.writeNonFatalException(thread, nonFatal);
216-
controller.doCloseSessions(testSettingsProvider);
216+
crashlyticsWorkers.common.submit(() -> controller.doCloseSessions(testSettingsProvider));
217217

218218
crashlyticsWorkers.common.await();
219+
crashlyticsWorkers.diskWrite.await();
219220

220221
verify(mockSessionReportingCoordinator)
221222
.persistNonFatalEvent(eq(nonFatal), eq(thread), eq(sessionId), anyLong());
@@ -489,6 +490,8 @@ public void testUploadDisabledThenOptOut() throws Exception {
489490
await(controller.deleteUnsentReports());
490491
await(task);
491492

493+
crashlyticsWorkers.diskWrite.await();
494+
492495
verify(mockSessionReportingCoordinator).removeAllReports();
493496
verifyNoMoreInteractions(mockSessionReportingCoordinator);
494497
}

firebase-crashlytics/src/androidTest/java/com/google/firebase/crashlytics/internal/metadata/MetaDataStoreTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ public void testWriteUserData_escaped() throws Exception {
170170
crashlyticsWorkers.diskWrite.await();
171171
UserMetadata userData =
172172
UserMetadata.loadFromExistingSession(SESSION_ID_1, fileStore, crashlyticsWorkers);
173+
Thread.sleep(10);
173174
assertEquals(ESCAPED.trim(), userData.getUserId());
174175
}
175176

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/CrashlyticsController.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,6 @@ public Task<Void> call() throws Exception {
205205

206206
// We've fatally crashed, so write the marker file that indicates a crash occurred.
207207
crashMarker.create();
208-
209208
reportingCoordinator.persistFatalEvent(
210209
ex, thread, currentSessionId, timestampSeconds);
211210

@@ -363,8 +362,11 @@ public Task<Void> then(@Nullable Boolean send) throws Exception {
363362
public Task<Void> call() throws Exception {
364363
if (!send) {
365364
Logger.getLogger().v("Deleting cached crash reports...");
366-
deleteFiles(listAppExceptionMarkerFiles());
367-
reportingCoordinator.removeAllReports();
365+
crashlyticsWorkers.diskWrite.submit(
366+
() -> {
367+
deleteFiles(listAppExceptionMarkerFiles());
368+
reportingCoordinator.removeAllReports();
369+
});
368370
unsentReportsHandled.trySetResult(null);
369371
return Tasks.forResult(null);
370372
}
@@ -512,7 +514,7 @@ boolean finalizeSessions(SettingsProvider settingsProvider) {
512514

513515
Logger.getLogger().v("Finalizing previously open sessions.");
514516
try {
515-
doCloseSessions(true, settingsProvider);
517+
doCloseSessions(true, settingsProvider, true);
516518
} catch (Exception e) {
517519
Logger.getLogger().e("Unable to finalize previously open sessions.", e);
518520
return false;
@@ -557,15 +559,19 @@ private void doOpenSession(String sessionIdentifier, Boolean isOnDemand) {
557559
reportingCoordinator.onBeginSession(sessionIdentifier, startedAtSeconds);
558560
}
559561

562+
// This is only used for exception handler close session (we have another close session in
563+
// background initialization)
560564
void doCloseSessions(SettingsProvider settingsProvider) {
561-
doCloseSessions(false, settingsProvider);
565+
doCloseSessions(false, settingsProvider, false);
562566
}
563567

564568
/**
565-
* Not synchronized/locked. Must be executed from the single thread executor service used by this
566-
* class.
569+
*
570+
* Not synchronized/locked. Must be executed from the executor service runs tasks in serial order
567571
*/
568-
private void doCloseSessions(boolean skipCurrentSession, SettingsProvider settingsProvider) {
572+
private void doCloseSessions(
573+
boolean skipCurrentSession, SettingsProvider settingsProvider, boolean isInitProcess) {
574+
CrashlyticsWorkers.checkBackgroundThread();
569575
final int offset = skipCurrentSession ? 1 : 0;
570576

571577
// :TODO HW2021 this implementation can be cleaned up.
@@ -579,13 +585,15 @@ private void doCloseSessions(boolean skipCurrentSession, SettingsProvider settin
579585

580586
final String mostRecentSessionIdToClose = sortedOpenSessions.get(offset);
581587

582-
if (settingsProvider.getSettingsSync().featureFlagData.collectAnrs) {
588+
// We only collect ANR info for finalize report during initialization process
589+
if (isInitProcess && settingsProvider.getSettingsSync().featureFlagData.collectAnrs) {
583590
writeApplicationExitInfoEventIfRelevant(mostRecentSessionIdToClose);
584591
} else {
585592
Logger.getLogger().v("ANR feature disabled.");
586593
}
587594

588-
if (nativeComponent.hasCrashDataForSession(mostRecentSessionIdToClose)) {
595+
// We only collect native crash info for finalize report during initialization process
596+
if (isInitProcess && nativeComponent.hasCrashDataForSession(mostRecentSessionIdToClose)) {
589597
// We only finalize the current session if it's a Java crash, so only finalize native crash
590598
// data when we aren't including current.
591599
finalizePreviousNativeSession(mostRecentSessionIdToClose);

firebase-crashlytics/src/main/java/com/google/firebase/crashlytics/internal/common/SessionReportingCoordinator.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -354,12 +354,16 @@ private boolean onReportSendComplete(@NonNull Task<CrashlyticsReportWithSessionI
354354
CrashlyticsReportWithSessionId report = task.getResult();
355355
Logger.getLogger()
356356
.d("Crashlytics report successfully enqueued to DataTransport: " + report.getSessionId());
357-
File reportFile = report.getReportFile();
358-
if (reportFile.delete()) {
359-
Logger.getLogger().d("Deleted report file: " + reportFile.getPath());
360-
} else {
361-
Logger.getLogger().w("Crashlytics could not delete report file: " + reportFile.getPath());
362-
}
357+
crashlyticsWorkers.diskWrite.submit(
358+
() -> {
359+
File reportFile = report.getReportFile();
360+
if (reportFile.delete()) {
361+
Logger.getLogger().d("Deleted report file: " + reportFile.getPath());
362+
} else {
363+
Logger.getLogger()
364+
.w("Crashlytics could not delete report file: " + reportFile.getPath());
365+
}
366+
});
363367
return true;
364368
}
365369
Logger.getLogger()

firebase-crashlytics/src/test/java/com/google/firebase/crashlytics/internal/common/CrashlyticsControllerRobolectricTest.java

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.google.firebase.crashlytics.internal.settings.SettingsProvider;
4444
import com.google.firebase.inject.Deferred;
4545
import java.util.ArrayList;
46+
import java.util.Arrays;
4647
import java.util.Collections;
4748
import java.util.List;
4849
import java.util.TreeSet;
@@ -65,6 +66,8 @@ public class CrashlyticsControllerRobolectricTest {
6566
@Mock private SessionReportingCoordinator mockSessionReportingCoordinator;
6667
@Mock private DataCollectionArbiter mockDataCollectionArbiter;
6768

69+
private CrashlyticsWorkers crashlyticsWorkers;
70+
6871
private static final CrashlyticsNativeComponent MISSING_NATIVE_COMPONENT =
6972
new CrashlyticsNativeComponentDeferredProxy(
7073
new Deferred<CrashlyticsNativeComponent>() {
@@ -80,17 +83,24 @@ public void setUp() {
8083
MockitoAnnotations.openMocks(this);
8184
testContext = getApplicationContext();
8285
testFileStore = new FileStore(testContext);
86+
crashlyticsWorkers =
87+
new CrashlyticsWorkers(TestOnlyExecutors.background(), TestOnlyExecutors.blocking());
8388
}
8489

8590
@Test
86-
public void testDoCloseSession_enabledAnrs_doesNotPersistsAppExitInfoIfItDoesntExist() {
91+
public void testDoCloseSession_enabledAnrs_doesNotPersistsAppExitInfoIfItDoesntExist()
92+
throws Exception {
8793
final String sessionId = "sessionId";
8894
final CrashlyticsController controller = createController();
8995

9096
when(mockSessionReportingCoordinator.listSortedOpenSessionIds())
9197
.thenReturn(new TreeSet<>(Collections.singletonList(sessionId)));
9298
mockSettingsProvider(true, false);
93-
controller.doCloseSessions(mockSettingsProvider);
99+
100+
crashlyticsWorkers.common.submit(() -> controller.doCloseSessions(mockSettingsProvider));
101+
// cannot use await since it check preconditions if blocking main thread
102+
Thread.sleep(10);
103+
94104
// Since we haven't added any app exit info to the shadow activity manager, there won't exist a
95105
// single app exit info, and so this method won't be called.
96106
verify(mockSessionReportingCoordinator, never())
@@ -99,7 +109,8 @@ public void testDoCloseSession_enabledAnrs_doesNotPersistsAppExitInfoIfItDoesntE
99109
}
100110

101111
@Test
102-
public void testDoCloseSession_enabledAnrs_persistsAppExitInfoIfItExists() {
112+
public void testDoCloseSession_enabledAnrs_persistsAppExitInfoIfItExists() throws Exception {
113+
final String sessionIdPrevious = "sessionIdPrevious";
103114
final String sessionId = "sessionId";
104115
final CrashlyticsController controller = createController();
105116
// Adds multiple AppExitInfos to confirm that Crashlytics loops through
@@ -109,26 +120,30 @@ public void testDoCloseSession_enabledAnrs_persistsAppExitInfoIfItExists() {
109120
List<ApplicationExitInfo> testApplicationExitInfo = getApplicationExitInfoList();
110121

111122
when(mockSessionReportingCoordinator.listSortedOpenSessionIds())
112-
.thenReturn(new TreeSet<>(Collections.singletonList(sessionId)));
123+
.thenReturn(new TreeSet<>(Arrays.asList(sessionId, sessionIdPrevious)));
113124
mockSettingsProvider(true, false);
114-
controller.doCloseSessions(mockSettingsProvider);
125+
crashlyticsWorkers.common.submit(() -> controller.finalizeSessions(mockSettingsProvider));
126+
// cannot use await since it check preconditions if blocking main thread
127+
Thread.sleep(100);
115128
verify(mockSessionReportingCoordinator)
116129
.persistRelevantAppExitInfoEvent(
117-
eq(sessionId),
130+
eq(sessionIdPrevious),
118131
eq(testApplicationExitInfo),
119132
any(LogFileManager.class),
120133
any(UserMetadata.class));
121134
}
122135

123136
@Test
124-
public void testDoCloseSession_disabledAnrs_doesNotPersistsAppExitInfo() {
137+
public void testDoCloseSession_disabledAnrs_doesNotPersistsAppExitInfo() throws Exception {
125138
final String sessionId = "sessionId";
126139
final CrashlyticsController controller = createController();
127140

128141
when(mockSessionReportingCoordinator.listSortedOpenSessionIds())
129142
.thenReturn(new TreeSet<>(Collections.singletonList(sessionId)));
130143
mockSettingsProvider(false, false);
131-
controller.doCloseSessions(mockSettingsProvider);
144+
crashlyticsWorkers.common.submit(() -> controller.doCloseSessions(mockSettingsProvider));
145+
// cannot use await since it check preconditions if blocking main thread
146+
Thread.sleep(10);
132147
verify(mockSessionReportingCoordinator, never())
133148
.persistRelevantAppExitInfoEvent(
134149
eq(sessionId), any(), any(LogFileManager.class), any(UserMetadata.class));
@@ -177,7 +192,7 @@ private CrashlyticsController createController() {
177192
MISSING_NATIVE_COMPONENT,
178193
mock(AnalyticsEventLogger.class),
179194
mock(CrashlyticsAppQualitySessionsSubscriber.class),
180-
new CrashlyticsWorkers(TestOnlyExecutors.background(), TestOnlyExecutors.blocking()));
195+
crashlyticsWorkers);
181196
controller.openSession(SESSION_ID);
182197
return controller;
183198
}

0 commit comments

Comments
 (0)