Repository navigation
ci(shards): adopt the stable module assignment for both suites (#14111) - #14648
Conversation
Review — APPROVE-WITH-NITS (#14646 and #14648 as a pair)Merge order is #14646 first, then #14648 — the latter is stacked on the former's branch and references The mechanism is sound and the stability guard is behavioural, not a source-text grep — it builds the table once, computes assignments under two different collected sets, and asserts equality, and asserts the inverse property so it cannot be satisfied by a function returning a constant. The handoff notes record an earlier wrong version of that same test (rebuilt the table from the mutated collection, defeating the point), which is evidence the guard was exercised against a failing case rather than written to pass. "Stable" is a narrower claim than it reads — please state it preciselyVerified: assignment survives adding a test, adding or removing a module, partial collection, and collection order, because It does not survive a file rename — inherent to a path hash, arguably fine, but untested and unstated. And it is stable only between durations regenerations: the weekly cron rebuilds The empty-shard question: not today, and the tolerance is one-sidedIndependently reproduced from the real durations files — backend 1282 modules / 22087 tests, slm 106 / 1501. With ≥12 non-empty buckets the greedy LPT loop necessarily lands the first 12 on distinct shards, so no shard can be empty at these counts. But the exit-5 tolerance that turns an empty shard into a notice rather than a red run exists only for the slm invocation. The backend invocation has no The finding I would most want fixed before merge
Also state the one-time reshuffle#14648 swaps the algorithm wholesale, so every module's shard membership very likely changes on this merge. One sentence in the body or the Balance, for the recordThe 1.18x spread is structural rather than noise: Nits
And the enforcement reality, stated plainly: |
|
#14646 is merged ( My review is posted above. The one item I would genuinely like closed before this merges, because it goes stale the instant it does:
Two smaller ones for the body rather than the code: say that this merge is a one-time full reshuffle of shard membership, so any prior "shard N is flaky" history is void — otherwise a future bisect chases a ghost. And note that the exit-5 empty-shard tolerance exists only for the slm invocation; the backend one has none. Safe at 1282 modules, but the "announced rather than red" property this advertises is currently true for one suite of two. |
|
Correction to my previous comment — code spans were dropped by a quoting mistake on my end. The intended references:
Nothing else in that comment changed. I built the body with an unquoted shell string and the backticked spans were evaluated rather than passed through; the rest of my reviews use a quoted heredoc, which is why they are intact. |
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
|
Root-caused and fixed — closing out my earlier diagnosisI left a comment saying the leak was localised but not root-caused: the autouse
That is why a The Verified independently:
Worth stating because green alone would not have proved it: this leak surfaces only |
…nnot collapse (#14802) (#14803) A zero-weight bucket adds nothing to shard_load, so the load-only tie-break handed the same lowest-index shard every subsequent tie and every weightless bucket piled onto it. With an empty durations file all 512 buckets landed on shard 0; with one module, on shards 0 and 1. It is structural rather than transient: assignment is by hash, so shards that receive nothing stay unreachable by any file added later. 5000 hypothetical new modules reached only shards 0 and 1. Invisible until now because the committed durations describes ~1282 modules, so every shard is used and the spread is unchanged at 1.001x. The existing test asserted set(table) <= set(range(SPLITS)), which holds when one shard of twelve is used, so the case read as covered while being untested. It now asserts equality, with no-durations, one-module and five-module cases and a check that future modules still reach every shard. All four fail against the old tie-break. Closes #14802. Unblocks #14648, which is what activates this algorithm in CI.
Thinking Path
Stacked on #14646, which adds the assignment and its tests but wires nothing. This is the half that gives it effect, kept separate because it edits
ci.ymlnext to a note that says "Do not switch it" — that deserves its own review rather than riding along.The note is not violated. It warns against
least_duration, which scatters individual tests across shards and splits modules — the same fixture-sharing hazard that made--dist workstealworse. A module-level assignment keeps modules whole, which is precisely the property the note exists to protect. The note is rewritten to say what is now true and why the rule still holds.What Changed
Both shard invocations, backend and slm:
-n auto --dist loadscopeis untouched, deliberately. Sharding composes with it exactly as before: the plugin deselects everything outside the group at collection time, and xdist distributes what remains by scope.The
#13637exit-5 note is also updated. It said "Fixing the packing is the real repair" — this is that repair, so leaving it would have left a stale claim. The tolerance is kept anyway: it costs nothing, and the condition it guards is a property of the module set rather than the algorithm. A target with fewer modules than shards still empties one, and that must stay announced rather than red.Verification
Run end to end against real targets, not just reasoned about.
The plugin selects exactly what the offline table predicts. For
autobot-backend/a2a, computing the assignment from.test_durationspredicts groups 6/7/8/11 with 33/46/53/48 rows. Running the real plugin under real pytest:That is the check that matters, because it proves pytest's node-ID module paths and the durations-file keys are the same shape. Had they diverged, assignment would still have been stable while silently ignoring every weight — balance would degrade with nothing failing.
No empty shards on either real target set:
The slm figure is the direct answer to #13637, which measured group 1 taking 497 of 1644 while groups 8-12 took none.
ci.ymlvalidated as YAML after editing.Not verified here: wall-clock shard times under the new distribution. Balance is asserted on test count, which is the better proxy than recorded duration but still a proxy — the first real CI run on this PR is the measurement.
Model Used
Opus 5
Closes #14111