Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ internal class CoreFeature(
contextExecutorService.queue.drainTo(contextTasks)

contextExecutorService.shutdown()
contextExecutorService.awaitTermination(DRAIN_WAIT_SECONDS, TimeUnit.SECONDS)
awaitTerminationLogged(contextExecutorService, "contextExecutorService", DRAIN_WAIT_SECONDS, TimeUnit.SECONDS)
Comment thread
abrooksv marked this conversation as resolved.
contextTasks.forEach {
it.run()
}
Expand All @@ -376,14 +376,38 @@ internal class CoreFeature(
persistenceExecutorService.shutdown()
uploadExecutorService.shutdown()

persistenceExecutorService.awaitTermination(DRAIN_WAIT_SECONDS, TimeUnit.SECONDS)
uploadExecutorService.awaitTermination(DRAIN_WAIT_SECONDS, TimeUnit.SECONDS)
awaitTerminationLogged(
persistenceExecutorService,
"persistenceExecutorService",
DRAIN_WAIT_SECONDS,
TimeUnit.SECONDS
)
// uploadExecutorService can be mid-upload when this runs, and only NETWORK_TIMEOUT_MS
// bounds how long that upload can take. Failing to wait long enough here can lead to
// a DataFlusher race where it uploads the same batch twice. See RUM-18168.
awaitTerminationLogged(
uploadExecutorService,
"uploadExecutorService",
NETWORK_TIMEOUT_MS,
TimeUnit.MILLISECONDS
)

ioTasks.forEach {
it.run()
}
}

@Suppress("UnsafeThirdPartyFunctionCall") // Used in Nightly tests only

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

probably comment is wrong, we don't have nightlies anymore

private fun awaitTerminationLogged(executor: ExecutorService, executorName: String, wait: Long, unit: TimeUnit) {
if (!executor.awaitTermination(wait, unit)) {
internalLogger.log(
InternalLogger.Level.WARN,
InternalLogger.Target.MAINTAINER,
{ "drainAndShutdownExecutors: $executorName did not terminate within $wait $unit" }
)
}
}

// region Internal

@WorkerThread
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1555,10 +1555,39 @@ internal class CoreFeatureTest {
// Then
inOrder(mockUploadService) {
verify(mockUploadService).shutdown()
verify(mockUploadService).awaitTermination(10, TimeUnit.SECONDS)
verify(mockUploadService).awaitTermination(CoreFeature.NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)
}
}

@Test
fun `M log a warning W drainAndShutdownExecutors() { upload executor doesn't terminate in time }`() {
// Given
testedFeature.initialize(
appContext.mockInstance,
fakeSdkInstanceId,
fakeConfig,
fakeConsent
)

val blockingQueue = LinkedBlockingQueue<Runnable>()
val mockUploadService: ScheduledThreadPoolExecutor = mock()
whenever(mockUploadService.queue).thenReturn(blockingQueue)
whenever(mockUploadService.awaitTermination(any(), any())) doReturn false
testedFeature.uploadExecutorService = mockUploadService

// When
testedFeature.drainAndShutdownExecutors()

// Then
mockInternalLogger.verifyLog(
InternalLogger.Level.WARN,
InternalLogger.Target.MAINTAINER,
"drainAndShutdownExecutors: uploadExecutorService did not terminate " +
"within ${CoreFeature.NETWORK_TIMEOUT_MS} ${TimeUnit.MILLISECONDS}",
mode = atLeastOnce()
)
}

@Test
fun `M shutdown with wait the context executor W drainAndShutdownExecutors()`() {
// Given
Expand Down
Loading