mirror of
https://github.com/morluto/rea.git
synced 2026-11-14 02:32:54 +00:00
test(ci): retire loader-ownership history seeding (#10404)
* test(ci): retire loader-ownership history seeding Both timing tiers now publish javascriptLiteralLoaderOwnership.test.ts's own duration (fast 23.7s, full 26.6s), so the seeding returned its input unchanged. Remove it and its tests. * docs(ci): describe shard weighting without retired loader seeding
This commit is contained in:
1 parent
3403338ea0
commit
da91bbd4eb
3 files changed
+8
-146
No files matched your search
+6
-14
@@ -850,20 +850,12 @@ the sequencer's two-worker schedule balances every shard at about 404 seconds
|
||||
of file time. The same replay with the stale history weights leaves the slowest
|
||||
shard near 547 seconds and the fastest near 293. That spread is file-schedule
|
||||
time, not runner queue, which is why each tier keeps its own history.
|
||||
CI assigns discovered files by historical duration, with a deterministic
|
||||
fallback weight for new files, rather than dividing only by file count.
|
||||
Each file keeps its full duration on one worker. Extra workers overlap other
|
||||
files in the same sequence group, and `process-boundary` runs after that
|
||||
group. History published before the competing-loader split still names only
|
||||
`javascriptLiteralPercentPaths.test.ts`. Until an artifact records
|
||||
`javascriptLiteralLoaderOwnership.test.ts`, the sequencer assigns that
|
||||
recorded duration by the pre-split case counts: 54/192 stays with the
|
||||
percent-path file and 138/192 goes to the consolidated loader file. History
|
||||
that names the retired Early, Middle, and Late shards sums those durations
|
||||
onto the consolidated file instead, and scales a partial set as three equal
|
||||
shares. That weight is the old dual-surface cost. The demoted file runs every
|
||||
loader once through the shared stdio MCP session, so the first schedule
|
||||
overestimates it. Every discovered file still belongs to exactly one shard;
|
||||
CI assigns discovered files by historical duration rather than dividing only
|
||||
by file count. A file with no recorded duration, such as a newly added or
|
||||
renamed test, is weighted at the median recorded duration until a published
|
||||
artifact records it. Each file keeps its full duration on one worker. Extra
|
||||
workers overlap other files in the same sequence group, and `process-boundary`
|
||||
runs after that group. Every discovered file still belongs to exactly one shard;
|
||||
worker limits and project ordering remain unchanged. Fast qualification keeps
|
||||
eight built shards without coverage. The merge job rejects successful reports
|
||||
with missing, unexpected, or duplicate portable files. Coverage instrumentation
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
parseTestDurations,
|
||||
readTestDurations,
|
||||
scheduleWall,
|
||||
seedLoaderOwnershipDurations,
|
||||
} from "../../ciTestSequencer.js";
|
||||
|
||||
describe("CI test history", () => {
|
||||
@@ -86,72 +85,6 @@ describe("CI test history", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("loader ownership history", () => {
|
||||
const source =
|
||||
"tests/acceptance/applications/javascriptLiteralPercentPaths.test.ts";
|
||||
const consolidated =
|
||||
"tests/acceptance/applications/javascriptLiteralLoaderOwnership.test.ts";
|
||||
const parts = [
|
||||
"tests/acceptance/applications/javascriptLiteralLoaderOwnershipEarly.test.ts",
|
||||
"tests/acceptance/applications/javascriptLiteralLoaderOwnershipMiddle.test.ts",
|
||||
"tests/acceptance/applications/javascriptLiteralLoaderOwnershipLate.test.ts",
|
||||
];
|
||||
|
||||
it("assigns the pre-split loader share to the consolidated file", () => {
|
||||
const seeded = seedLoaderOwnershipDurations(
|
||||
new Map([
|
||||
[source, 192_000],
|
||||
["tests/other.test.ts", 10],
|
||||
]),
|
||||
);
|
||||
expect(seeded.get(source)).toBe(54_000);
|
||||
expect(seeded.get(consolidated)).toBe(138_000);
|
||||
for (const path of parts) expect(seeded.has(path)).toBe(false);
|
||||
expect(seeded.get("tests/other.test.ts")).toBe(10);
|
||||
expect(
|
||||
[...seeded.values()].reduce((sum, duration) => sum + duration, 0),
|
||||
).toBe(192_000 + 10);
|
||||
});
|
||||
|
||||
it("sums retired shard durations onto the consolidated file", () => {
|
||||
const seeded = seedLoaderOwnershipDurations(
|
||||
new Map([
|
||||
[source, 20_000],
|
||||
[parts[0]!, 30_000],
|
||||
[parts[1]!, 40_000],
|
||||
[parts[2]!, 50_000],
|
||||
["tests/other.test.ts", 10],
|
||||
]),
|
||||
);
|
||||
expect(seeded.get(source)).toBe(20_000);
|
||||
expect(seeded.get(consolidated)).toBe(120_000);
|
||||
expect(seeded.get("tests/other.test.ts")).toBe(10);
|
||||
});
|
||||
|
||||
it("scales a partial retired shard as three equal shares", () => {
|
||||
const seeded = seedLoaderOwnershipDurations(
|
||||
new Map([
|
||||
[source, 20_000],
|
||||
[parts[0]!, 30_000],
|
||||
]),
|
||||
);
|
||||
expect(seeded.get(source)).toBe(20_000);
|
||||
expect(seeded.get(consolidated)).toBe(90_000);
|
||||
});
|
||||
|
||||
it("keeps a published consolidated duration", () => {
|
||||
const recorded = new Map<string, number>([
|
||||
[source, 20_000],
|
||||
[parts[0]!, 30_000],
|
||||
[consolidated, 12_000],
|
||||
]);
|
||||
expect(seedLoaderOwnershipDurations(recorded)).toEqual(recorded);
|
||||
expect(
|
||||
seedLoaderOwnershipDurations(new Map([["tests/other.test.ts", 10]])),
|
||||
).toEqual(new Map([["tests/other.test.ts", 10]]));
|
||||
});
|
||||
});
|
||||
|
||||
describe("duration shard allocation", () => {
|
||||
it.prop([
|
||||
fc.uniqueArray(fc.integer({ min: 0, max: 10_000 }), {
|
||||
|
||||
@@ -60,68 +60,6 @@ export async function readTestDurations(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Case counts from the competing-loader file before it was split: 54 cases
|
||||
* stayed in the percent-path file and 46 ran in each of the three dual-surface
|
||||
* shards. History that still names only the original path is that combined
|
||||
* cost. The shards are retired; one file now runs every loader through stdio
|
||||
* MCP plus a few compiled CLI parity cases.
|
||||
*/
|
||||
const LOADER_HISTORY_SOURCE =
|
||||
"tests/acceptance/applications/javascriptLiteralPercentPaths.test.ts";
|
||||
const LOADER_HISTORY_PARTS = [
|
||||
"tests/acceptance/applications/javascriptLiteralLoaderOwnershipEarly.test.ts",
|
||||
"tests/acceptance/applications/javascriptLiteralLoaderOwnershipMiddle.test.ts",
|
||||
"tests/acceptance/applications/javascriptLiteralLoaderOwnershipLate.test.ts",
|
||||
] as const;
|
||||
const LOADER_HISTORY_CONSOLIDATED =
|
||||
"tests/acceptance/applications/javascriptLiteralLoaderOwnership.test.ts";
|
||||
const LOADER_HISTORY_REMAINDER_CASES = 54;
|
||||
const LOADER_HISTORY_PART_CASES = 46;
|
||||
const LOADER_HISTORY_CASES =
|
||||
LOADER_HISTORY_REMAINDER_CASES +
|
||||
LOADER_HISTORY_PART_CASES * LOADER_HISTORY_PARTS.length;
|
||||
|
||||
/**
|
||||
* Charge the consolidated loader file until its own duration is published.
|
||||
* A pre-split percent-path duration keeps 54/192 and gives 138/192 to the
|
||||
* consolidated file. Recorded Early/Middle/Late durations are summed instead,
|
||||
* and a partial set is scaled as three equal shares. That weight is the old
|
||||
* dual-surface cost, so the first schedule overestimates the demoted file.
|
||||
* A published consolidated duration stands, including beside retired shards.
|
||||
*/
|
||||
export function seedLoaderOwnershipDurations(
|
||||
durations: ReadonlyMap<string, number>,
|
||||
): Map<string, number> {
|
||||
if (durations.has(LOADER_HISTORY_CONSOLIDATED)) return new Map(durations);
|
||||
const recordedParts = LOADER_HISTORY_PARTS.flatMap((path) => {
|
||||
const duration = durations.get(path);
|
||||
return duration === undefined ? [] : [duration];
|
||||
});
|
||||
if (recordedParts.length > 0) {
|
||||
const seeded = new Map(durations);
|
||||
const sum = recordedParts.reduce((total, duration) => total + duration, 0);
|
||||
seeded.set(
|
||||
LOADER_HISTORY_CONSOLIDATED,
|
||||
(sum * LOADER_HISTORY_PARTS.length) / recordedParts.length,
|
||||
);
|
||||
return seeded;
|
||||
}
|
||||
const combined = durations.get(LOADER_HISTORY_SOURCE);
|
||||
if (combined === undefined) return new Map(durations);
|
||||
const seeded = new Map(durations);
|
||||
const loaderCases = LOADER_HISTORY_CASES - LOADER_HISTORY_REMAINDER_CASES;
|
||||
seeded.set(
|
||||
LOADER_HISTORY_SOURCE,
|
||||
(combined * LOADER_HISTORY_REMAINDER_CASES) / LOADER_HISTORY_CASES,
|
||||
);
|
||||
seeded.set(
|
||||
LOADER_HISTORY_CONSOLIDATED,
|
||||
(combined * loaderCases) / LOADER_HISTORY_CASES,
|
||||
);
|
||||
return seeded;
|
||||
}
|
||||
|
||||
type TimedItem<T> = {
|
||||
item: T;
|
||||
key: string;
|
||||
@@ -263,9 +201,8 @@ export class CiTestSequencer extends BaseSequencer {
|
||||
const shard = this.ctx.config.shard;
|
||||
if (!historyPath || !shard) return super.shard(files);
|
||||
const root = this.ctx.config.root;
|
||||
const recorded = await readTestDurations(historyPath, root);
|
||||
if (!recorded) return super.shard(files);
|
||||
const durations = seedLoaderOwnershipDurations(recorded);
|
||||
const durations = await readTestDurations(historyPath, root);
|
||||
if (!durations) return super.shard(files);
|
||||
|
||||
const weighted = files.map((item) => {
|
||||
const path = relative(root, resolve(root, item.moduleId))
|
||||
|
||||
Reference in new issue
Block a user