Fix #2032 & #3714: stabilize AndroidX workers #3716
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Explanation
Fix #2032
Fix #3714
Stabilizes both PlatformParameterSyncUpWorker & LogUploadWorker by using a ListenableWorker instead of CoroutineWorker. In order to ensure the tests aren't flaky, we have to fully control the execution pipeline for WorkManager. We get halfway there by using a SynchronousExecutor when setting up the work manager, but CoroutineExecutor has a "hop" using Kotlin's default executor before
doWork()
is called. Even though we later usewithContext
to hop over to our own background executor, that little hop is on an unmanaged thread that we cannot synchronize against in the test (which results in flakes).The fix is to use ListenableWorker directly and immediately kick-off a coroutine using our own dispatcher, avoiding any hops. With this setup, the test environment utilizing both SynchronousExecutor and TestCoroutineDispatchers results in a fully managed & deterministic environment. Fortunately, there should be more or less no change to the workers' production behavior since the new implementation added here is close to equivalent to CoroutineWorker (at least for
doWorker
; there are other aspects of CoroutineWorker that differ which we are ignoring--I think this is fine).One note on correctness: an issue with both the new implementation of the workers & CoroutineWorker is neither anticipate the potential of the coroutine task indefinitely hanging. We really ought to introduce a
withTimeout
, but we can't in a way that won't reintroduce a potential flake in the test until #3715 is resolved. Given this isn't a change in existing prod behavior, I suspect that it's fine.To verify that this fix works, I ran both workers' test suites 100 times locally and saw no failures (previously, I would see about ~20 failures for PlatformParameterSyncUpWorker and ~12 for LogUploadWorker).
Finally, to prevent this again in the future I introduced a new pattern check to prohibit using CoroutineWorker. I also added one for SettableFuture for good measure since it's challenging to use correctly, though I suspect this will rarely be hit since we prefer using coroutines over ListenableFutures.
Checklist