Repository navigation
feat(test-consume): start the next client while the current group runs in enginex and wirex - #3952
Merged
danceratopz merged 3 commits intoNov 4, 2026
Conversation
…s in enginex and wirex A worker now starts the client of the next pre-allocation group queued to it on a background thread as soon as its current group's client is ready, and the next group's first test claims it instead of starting its own. A worker holds at most one client started ahead. On by default for consume enginex and consume wirex; --no-pipeline-client-start turns it off. xdist's loadgroup scheduler hands a worker its next group only when it has two or fewer tests left, so the next group is rarely known in time. GroupLookaheadScheduling keeps one more group queued per worker, handed out once the current group has at most 100 tests left, so a long group cannot hold another back from idle workers. The remaining clients are now stopped when the module ends, before the shared hive test they belong to, so a start still running when a run is interrupted cannot outlive it.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## forks/bogota #3952 +/- ##
=============================================
Coverage 95.10% 95.10%
=============================================
Files 673 673
Lines 40904 40904
Branches 3778 3778
=============================================
Hits 38903 38903
Misses 1393 1393
Partials 608 608
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
bt-recneps
reviewed
Nov 2, 2026
bt-recneps
left a comment
Contributor
There was a problem hiding this comment.
Lookahead scheduling works on real xdist workers, but the wirex env carry-over is untested and the docs' container cap misses wirex :)
Reviewed in 10m 15s
…ironment additions Start the next group's client in a unit test and check its environment keeps what a simulator adds to or overrides in the environment fixture (wirex's sync loop interval), while the group-specific part comes from the next group.
A wirex multi-target fixture keeps its isolated clients alive next to the client started ahead, so the cap of two clients per worker holds for consume enginex only.
spencer-tb
approved these changes
Nov 4, 2026
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.

Description
In
consume enginexandconsume wirex, each xdist worker starts one client per pre-allocation group, runs the group's tests, stops it, and starts the next one. On the hive-tests mainnet dashboard (8 vCPU runners,--sim.parallelism=4), workers spend most of their time between containers waiting for that start: in consume enginex, besu starts in 7.6 s and nethermind in 3.9 s per group, and workers spend 73 to 78% of their time between containers for those clients.This PR overlaps the start with the current group's tests:
-n 4has at most 8 client containers alive.loadgrouphands a worker its next group only when it has two or fewer tests left, and most groups have one or two tests, so the next group is rarely known in time.GroupLookaheadScheduling(throughpytest_xdist_make_scheduler) keeps one more group queued per worker, handed out once the current group has at most 100 tests left, so a long group cannot hold another back from idle workers. Groups still run whole on one worker.replacement_reason) applies to it unchanged, and isolated multi-target clients never use it.It is on by default for both simulators.
--no-pipeline-client-startturns it off, for example to debug a client start or on a host short of memory. Since the dashboards run the published simulator images, they pick it up without a hive or hive-tests change.Measurements
Think of it as a pit crew warming up the next car while the current one is still on the track. Local A/B on a host pinned to the CI runner layout (4 physical cores with SMT: hive, consume and every client container confined to the same 8 threads),
-n 4,tests@v21.0.2, client images pinned by digest, runs interleaved off and on.Latest, with a cleaned-up Docker store (like CI, which starts each job with an empty one) and the pending hive start fixes (output attach, anonymous volumes, client wrappers):
fork_Cancun(12,875 tests)fork_CancunEarlier, before this round of host and hive fixes, when a container start and stop cost about twice as much on this host:
fork_Cancunfork_Cancunfork_Cancuntests/(cancun|prague)atfork_Prague(Means of one to three runs per arm.) No test changed verdict between off and on in any of the more than 50 runs, no client container was left running, and no client stop failed. The cheaper the start, the less there is to hide, so the latest table is the better guide to CI: expect a few percent for most clients, more where the client's own boot is slow.
¹ Besu's start is CPU-bound (about 16 s here, 7.6 s on CI) and longer than most groups, so only about 30% of it is hidden, and the boot running in the background competes with the tests next to it for the same 8 threads: workers wait less for starts but run tests more slowly by almost as much. Running more besu clients at once does not help either: at
-n 8with pipelining off, besu enginex takes about the same time as at-n 4with it on.Notes for review
multi_test_client.py(pre-alloc group, client genesis, environment) and the hive start are moved out of their fixtures into plain functions, so the start ahead can build the next group's inputs outside pytest's fixture graph. Fixture behaviour is unchanged.environmentfixture (wirex's sync loop interval).next_group_itemreads the last queued item first and only scans the queue once another group has arrived. Without that, every test of a long group would scan the group's remaining tests while it waits for the lookahead (quadratic; it added about 2 minutes to a wirex run with one 11,000-test group).__channelexec__, so the worker's test queue is found by its attributes rather than byisinstance(…, WorkerInteractor).pyproject.tomlgains a mypy override forxdist.*, which ships no type information.Related Issues or PRs
N/A.
Checklist
just static<type>(<area>): <description>, where<type>and<area>come from an appropriateC-<type>, respectivelyA-<area>, label. The title should match the target squash commit message.Cute Animal Picture
No picture, but here is a snail that learned to start its engine before the light turns green: 🐌💨