Skip to content

CI: the test-polars py3.12 coverage cell is at its timeout ceiling; every polars test addition re-triggers a TIMEOUT reported as cancelled #1797

Description

@lmeyerov

Summary

The test-polars py3.12 cell is at its wall-clock ceiling. It is the only cell in the matrix that runs coverage, and it shares one timeout-minutes with five siblings that do half its work. Adding a single test file to bin/test-polars.sh is now enough to push it over, and when it goes over GitHub reports the job as cancelled — which reads as unrelated fail-fast collateral, not as a budget failure.

This is a structural capacity limit, not a one-off. It will re-fire on the next polars test addition.

Evidence

On PR #1794 (which appends one file to POLARS_TEST_FILES), test-polars (3.12) failed twice with zero test failures:

attempt duration outcome
2 10m15s cancelled
3 10m11s cancelled

Job log, attempt 3:

2377 passed, 13 skipped ... in 572.43s (0:09:32)
The operation was canceled.

The pytest phase alone was 9m32s of a 10m job budget. Siblings (3.9/3.10/3.11/3.13/3.14) finish in 4m09s–5m51s. A rerun does not clear it — it is 0-for-2.

Two things made this expensive to diagnose:

  1. A timed-out job is reported as cancelled, indistinguishable at a glance from fail-fast collateral. The distinguishing evidence is the duration: a fail-fast cancel lands seconds after a sibling failure; a timeout lands at exactly timeout-minutes. Attempt 2 was 1 cancelled / 72 success / 1 skipped, so collateral was ruled out by evidence.
  2. changed-line-coverage then reports SKIPPED, because it needs: the job that produces polars-coverage-py3.12. So one budget overrun silently disables a second gate.

Root cause

.github/workflows/ci.yml:

  • test-polars declares timeout-minutes: 10 for the whole matrix job (was line 1579).
  • Only the py3.12 cell runs Polars tests with coverage (POLARS_COV=1, was line 1618) plus the per-file Polars GFQL coverage audit plus an artifact upload.

So one cell carries roughly double the work of its siblings against a budget sized for the siblings. The timeout is simultaneously too tight for the cell that matters and too loose to be a useful hang detector for the ones that don't.

Fix applied in #1794

Split the coverage pass into its own job, test-polars-coverage (py3.12, timeout-minutes: 20), and drop 3.12 from the test-polars matrix. Net effect:

  • test-polars cells now do identical work, so its 10-minute budget is a real hang detector again.
  • The coverage pass has an explicit, separately visible budget that can grow without cancelling five unrelated cells.
  • No python version loses a run: py3.12 was already only running the coverage step, so this is the previous step layout re-homed.
  • changed-line-coverage needs: updated to include the new job (it consumes that job's artifact).

Note there is no required_status_checks configuration on master (verified via the branches API), so introducing a new check name does not block merges. If required checks are added later, test-polars-coverage should be in the list.

Still open after that fix

The 20 minutes buys headroom, it does not remove the ceiling. The polars lane is:

Candidate follow-ups, roughly in order of value:

  1. Add -n auto to the polars pytest invocations (coverage under xdist works via coverage combine; needs pytest-xdist confirmed in the test-polars-* locks).
  2. Shard POLARS_TEST_FILES across two coverage cells and combine the two .coverage files before the audit.
  3. Have the coverage lane report its own wall-clock into the step summary, so approaching the ceiling is visible before it fails.

Acceptance

  • A polars test-file addition of typical size does not cancel any CI job.
  • A cancelled polars job is attributable to a real cause, not to a shared budget.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions