Skip to content

perf(ci): start the serialized integration collection first - #861

Merged
mforce merged 2 commits into
mainfrom
perf/ci-integration-wall-clock-839
Sep 14, 2026
Merged

mforce merged 2 commits into
mainfrom
perf/ci-integration-wall-clock-839

Conversation

@mforce

@mforce mforce commented Sep 14, 2026

Copy link
Copy Markdown
Owner

What this is

Measurement for #839, plus one small scheduling change. Read the numbers before the title's verb — this does not make the suite faster. It stops the suite sometimes being much slower than it needs to be.

docs/plans/839-integration-wall-clock/measurement.md is the deliverable. The code is two files.

The measurement

Local, Ryzen 5 6600H / 12 logical CPUs / 18.8 GiB, cached images, Docker 29.8.0, .NET SDK 10.0.112.

Run Config Wall Tests
main (18b45dc) Debug 320s 1,801 passed
this branch Debug 326s 1,815 passed
this branch Release 341s 1,815 passed

Full-suite counts differ because the branch is rebased past #820 and #844.

What the ~5.5 minutes is made of (instrumented run, 331.5s wall):

  • Container readiness: 156.6 distinct wall-clock seconds, overlapping test execution. 119 Postgres + 7 Redis + 1 Ryuk.
  • The shared integration collection: 1,024 of 1,815 tests, one fixture, running serially end to end.
  • Three one-shot-process classes dominate: SeedCommandTests 131.14s, ProcessRoleGuardTests 127.26s, OneShotVerbMinimalConfigTests 119.05s. Repeated app process startup, not SQL.

Two premises in the issue did not survive

  1. Container reuse is already done. Lever 1 assumed per-class startup was the dominant cost. The shared collection already holds one ICollectionFixture<CluckworkWebApplicationFactory> covering 1,024 tests. The remaining 119 Postgres containers are the specialized factories that genuinely need their own database, migration state, or advisory-lock behaviour.
  2. The remaining cost is subprocess, not SQL. The issue lists container reuse and parallelism. The measured cost is repeated one-shot process startup in three classes, which the issue never names.

The change

IntegrationCollectionOrderer puts the shared collection first and delegates every other collection to xUnit's default orderer. Fixture ownership, concurrency limits, serialization, and the race assertions are untouched — StealLossConnectionReleaseTests keeps its dedicated factory, one-slot pool, and timing assertions.

Why it is variance, not speedup: xUnit 2.9.3 documents its default collection order as unstable between runs. When the 95-class serialized collection draws a late slot, its ~300s of serial work becomes a tail nothing else can overlap. Pinning it first removes that schedule. It makes no test cheaper.

The first draft of this PR claimed 25.8% (434.51s → 322.46s). That compared against a single slow run. A second run of the unmodified scheduler came in at 327.06s, and the paired runs above put both versions inside each other's spread. The correction is in the measurement doc, not quietly dropped.

Also in here

  • tools/test-timing/ — measure.py captures wall clock, TRX, and Docker events; summarize.py reports per-class cost, container readiness, and fixture phases. Opt-in timing in the base factory behind CLUCKWORK_TEST_TIMING=1.
  • Two guard collisions found while building the tooling, both fixed in the tooling, neither guard weakened — worth knowing because they will bite the next person who adds a file under tools/:
    • the #508 tracked-file image-pin guard read a Python dict key as a live image reference (fixed by naming the variable reference);
    • the same guard's bare-postgres: literal detector fired on a timing phase label (fixed by naming the phase container).

Verification

  • Solution build: 0 warnings, 0 errors.
  • Integration suite on this branch: 1,815/1,815 passed in Debug and Release.
  • Domain 491/491, Application 290/290.
  • tools/test-timing/measure.py driven end to end on the branch as it now stands: exit 0, 1,815 passed, summary regenerated.
  • CI is not measured. Local results do not establish a CI improvement or an optimal worker count on hosted runners.

Not done

No test deletion, no SQLite, no higher parallelism, no product change. Remaining levers, in the order the data now suggests: the three subprocess classes; splitting the shared collection after auditing its shared-state assumptions; CI-side measurement.

Closes #839

Measure the suite before changing its scheduler. Prioritize the existing
serialized integration collection while preserving fixture ownership,
parallelism limits, and the default ordering of other collections.

Add opt-in fixture phase timing and reusable TRX and Docker measurements.
Record the clean local comparison and diagnostic run-order variation.

Refs #839
The 25.8 percent figure compared one slow baseline run. Paired runs at the same configuration put the orderer inside run-to-run spread, so the change is recorded as a variance reduction. The same runs refute the issue's container-reuse premise and name the subprocess tests as the remaining lever.

Refs #839
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 27265f8e-66b3-421b-8f9f-adc06bbb38e9


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mforce
mforce merged commit 1dcc7f6 into main Sep 14, 2026
16 checks passed
@mforce
mforce deleted the perf/ci-integration-wall-clock-839 branch September 14, 2026 06:01
mforce pushed a commit that referenced this pull request Sep 16, 2026
🤖 I have created a release *beep* *boop*
---


## [0.1.2](v0.1.1...v0.1.2)
(2026-09-16)


### Features

* **data:** standardize business record chronology
([#820](#820))
([6231b31](6231b31))
* **infra:** optional leader-lease endpoint for pooled deploys
([#869](#869))
([e9bc6a7](e9bc6a7))
* **sim:** seed a second farm for the README dashboard capture
([#867](#867))
([de407c6](de407c6))
* **web:** adopt MUI, themed from the farm palette tokens
([#674](#674))
([#860](#860))
([6c83c5c](6c83c5c))
* **web:** convert Daily entry to MUI, field-first on the phone
([#888](#888))
([b66f8b8](b66f8b8))
* **web:** convert the Dashboard and app shell to MUI
([#829](#829))
([#883](#883))
([2e94277](2e94277))
* **web:** retire the Slack-blue link colour for ink + a rule underline
([#884](#884))
([c08f9d8](c08f9d8))
* **web:** serve a per-request CSP nonce so Emotion's styles apply under
style-src 'self'
([#874](#874))
([ba4e6f3](ba4e6f3))
* **web:** visual language theme overrides for the MUI revamp
([#864](#864))
([#882](#882))
([0bb6b73](0bb6b73))
* **web:** whole-app MUI baseline, theme policy guard and the
[#740](#740) phone action rule
([#823](#823))
([#871](#871))
([af565e4](af565e4))


### Bug fixes

* **auth:** fail closed on unresolved flock-scope actors
([#787](#787))
([#868](#868))
([16d0350](16d0350))
* **auth:** make farm configuration owner-only
([#870](#870))
([42f9036](42f9036))
* **e2e:** repoint the canary at the markup two PRs replaced
([#844](#844))
([18b45dc](18b45dc))
* **i18n:** tl glossary uses the standard passive of ilagay
([#813](#813))
([20dec10](20dec10)),
closes [#738](#738)
* **sim:** stop the k6-baseline EXIT trap masking a clean run as failed
([#838](#838))
([f5ec96f](f5ec96f))
* **web:** declare the rule tokens the Dashboard reads, and guard
undeclared custom properties
([#885](#885))
([5bead1f](5bead1f))


### Performance

* **ci:** start the serialized integration collection first
([#861](#861))
([1dcc7f6](1dcc7f6)),
closes [#839](#839)


### Documentation

* **auth:** record the OAuth 2.1 decision for MCP authentication
([#801](#801))
([0510854](0510854))
* **designs:** MUI revamp design doc, component map, layout system, IA
([#862](#862))
([da49481](da49481))
* **readme:** recapture the daily entry, reports and sales screenshots
([#865](#865))
([f18e336](f18e336))
* **specs:** correct the sales_order_items column list in §10.5
([#812](#812))
([afe4a02](afe4a02)),
closes [#737](#737)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: cluckwork-lockfix[bot] <309265648+cluckwork-lockfix[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CI: measure and cut the integration suite's wall clock (container reuse, parallelism)

1 participant