Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions packages/rstack/src/fmt/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ interface FmtWorkerPoolResult {
processedFileCount: number;
}

/** Benchmarks show stable scheduling gains only when at least eight workers share the queue. */
const minPriorityWorkers = 8;

/**
* Markdown parsing is consistently slower in representative repositories. Keep this signal
* narrow: parser overrides and file size can outweigh the extension, and deferring every JS/TS
* file could turn a large source file into the final straggler.
*/
const isMarkdown = (file: FmtFileRequest): boolean =>
file.path.endsWith('.md') || file.path.endsWith('.mdx');

/** Converts a formatter outcome into the shared per-file result. */
const runFmtFile = async (
file: FmtFileRequest,
Expand All @@ -41,6 +52,30 @@ const runFmtFile = async (
}
};

/** Starts slower Markdown parsers first while preserving order within both priority groups. */
const runPriorityFmtFiles = async (
files: FmtFileRequest[],
shouldWrite: boolean,
formatFile: FormatFile,
): Promise<FmtFileOutcome[]> => {
const priority: number[] = [];
const rest: number[] = [];

for (let index = 0; index < files.length; index++) {
(isMarkdown(files[index]) ? priority : rest).push(index);
}

const order = priority.concat(rest);
const outcomes = await Promise.all(
order.map((index) => runFmtFile(files[index], shouldWrite, formatFile)),
);
const results = new Array<FmtFileOutcome>(files.length);
for (let index = 0; index < order.length; index++) {
results[order[index]] = outcomes[index];
}
return results;
};

/** Processes files in a worker pool while preserving input order. */
const runFmtFilesInWorkerPool = async (
files: FmtFileRequest[],
Expand All @@ -51,9 +86,12 @@ const runFmtFilesInWorkerPool = async (
const workerPool = await createFmtWorkerPool(files.length, maxWorkers);

try {
const results = await Promise.all(
files.map((file) => runFmtFile(file, shouldWrite, workerPool.formatFile)),
);
const results =
workerPool.workerCount >= minPriorityWorkers
? await runPriorityFmtFiles(files, shouldWrite, workerPool.formatFile)
: await Promise.all(
files.map((file) => runFmtFile(file, shouldWrite, workerPool.formatFile)),
);
const processedFiles: FmtFileResult[] = [];
let processedFileCount = 0;

Expand Down
2 changes: 2 additions & 0 deletions packages/rstack/src/fmt/workerPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { FmtFileRequest } from './types.ts';
type FmtWorkerMethods = typeof import('./worker.ts');

interface FmtWorkerPool {
readonly workerCount: number;
formatFile: (
file: FmtFileRequest,
shouldWrite: boolean,
Expand Down Expand Up @@ -55,6 +56,7 @@ const createFmtWorkerPool = async (
}

return {
workerCount,
formatFile: (file, shouldWrite) => pool.run({ file, shouldWrite }, { name: 'formatFile' }),
terminate: () => pool.destroy(),
};
Expand Down
1 change: 1 addition & 0 deletions packages/rstack/tests/fmt/runnerWriteFailure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const mocks = rs.hoisted(() => ({
rs.mock('../../src/fmt/workerPool.ts', () => ({
createFmtWorkerPool: () =>
Promise.resolve({
workerCount: 1,
formatFile: () => Promise.reject(new Error('file write failed')),
terminate: () => {
mocks.terminateCalls++;
Expand Down