mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-11-05 07:37:30 +00:00
* feat(ci): rebalance test shards from recorded timings * fix(ci): preserve artifact retries for shard timings Find shard-plan generation by its stable step ID and clarify the native runner cohort count (#3748). Note: pre-existing reference-oracle parser type failures in two test_oracle_assets cases remain outside this change. * fix(ci): keep retry timing snapshots immutable Require the existing snapshot on reruns so a failed download cannot refresh history and change shard assignments. Cover the workflow contract and document recovery when the artifact is unavailable. * docs(ci): document shard timing profile selection * fix(ci): balance cold shard estimates consistently --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
52 lines
2.3 KiB
TypeScript
52 lines
2.3 KiB
TypeScript
import { statSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { BaseSequencer, type TestSpecification } from 'vitest/node';
|
|
import { partition, readTimings } from 'vitest-shard-balancer';
|
|
import { shardTimingKey, shardTimingsFile } from '../../scripts/shard-timings.js';
|
|
|
|
/**
|
|
* Cost-balanced shard sequencer (wired via `sequence.sequencer` in
|
|
* vitest.config.ts).
|
|
*
|
|
* Uses the package's duration partitioner with the frozen CI timing snapshot.
|
|
* Only shard() changes; BaseSequencer keeps groupOrder and local sorting.
|
|
* On a cold start, spread sequential/native work using a generic size estimate
|
|
* instead of the count split that previously clustered the slowest suites.
|
|
*/
|
|
export default class PerfSequencer extends BaseSequencer {
|
|
override async shard(specs: TestSpecification[]): Promise<TestSpecification[]> {
|
|
const shard = this.ctx.config.shard;
|
|
if (!shard) return specs;
|
|
const root = this.ctx.config.root;
|
|
const keyed = new Map(
|
|
specs.map((spec) => [shardTimingKey(root, spec.moduleId, spec.project.name), spec]),
|
|
);
|
|
if (keyed.size !== specs.length) throw new Error('Duplicate test specification in shard input');
|
|
const keys = [...keyed.keys()];
|
|
const timings = readTimings(path.resolve(root, shardTimingsFile()));
|
|
let groups = partition(keys, timings, shard.count);
|
|
if (!groups) {
|
|
const estimates = Object.fromEntries(
|
|
[...keyed].map(([key, spec]) => {
|
|
let sizeKb = 0;
|
|
try {
|
|
sizeKb = statSync(spec.moduleId).size / 1024;
|
|
} catch {
|
|
/* Virtual test module. */
|
|
}
|
|
const config = spec.project.config;
|
|
const sequential = 'fileParallelism' in config && config.fileParallelism === false;
|
|
// Keep all cold weights on the same scale: 1,000 points represents
|
|
// 20 seconds of sequential work. A millisecond value here would
|
|
// dwarf file size and pile the parallel cohort onto fewer shards.
|
|
return [key, (sequential ? 1_000 : 1) + sizeKb];
|
|
}),
|
|
);
|
|
groups = partition(keys, estimates, shard.count);
|
|
}
|
|
const selected = new Set(groups?.[shard.index - 1] ?? []);
|
|
return specs.filter((spec) =>
|
|
selected.has(shardTimingKey(root, spec.moduleId, spec.project.name)),
|
|
);
|
|
}
|
|
}
|