Skip to content

Compose Paging tests flake on low-core CI: Dispatchers.Default pool starvation from accumulated background coroutines #1005

Description

@torlando-agent

Summary

Compose UI tests that render lists via Jetpack Paging (collectAsLazyPagingItems()) flake on CI shard jobs with content not displayed / ComposeTimeoutException, deterministically once enough test classes share a single test fork. They pass on dev machines. MessagingScreenTest is the current victim (26 of its paged-content tests), surfaced when an added test class reshuffled the index % 4 shard partition and moved it into a busier shard.

Root cause

The kotlinx Dispatchers.Default pool is sized to the CPU count — ~2 on GitHub-hosted runners. Many unit tests start background coroutines (managers/pollers with delay() loops, etc.) and don't cancel them in teardown. These accumulate within a fork (capped by the existing setForkEvery(10)) and create enough scheduler churn on a 2-thread pool that Paging's AsyncPagingDataDiffer worker can't get serviced, so items never present and the assertions see an empty list.

Diagnosis (reproduced locally)

Shrinking the Default pool to CI size reproduces CI exactly:

  • -Dkotlinx.coroutines.scheduler.core.pool.size: full shard fails at pool ≤ 8, passes at pool = 16.
  • Thread dumps show Default workers parked (idle), not blocked — i.e. diffuse suspended-coroutine churn from many tests, not one blocking leaker. No single class/quarter reproduces; only the aggregate does (8+ contributors).

Mitigation applied

PR #906 pins a fixed Default-pool floor for unit tests in app/build.gradle.kts (independent of host core count), complementing the existing forkEvery(10). This unblocks CI but masks the underlying leaks.

Cleanup owed (this issue)

Audit unit tests that start background coroutines (CoroutineScope(Dispatchers.Default/IO), managers, pollers, collectors) and ensure they cancel/stop in @After. Once the leaks are gone, the pool floor can be lowered or removed. Detection harness: run any Compose+Paging test preceded by suspects under -Dkotlinx.coroutines.scheduler.core.pool.size=1 -D...max.pool.size=2 and watch for the timeout.

— filed by claude-opus-4-8

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions