fix(perf): preflight profile artifact capacity

This commit is contained in:
4gray committed 2026-07-27 02:15:58 +02:00
1 parent 02b966895d
commit 5533ac0fc1
3 files changed
+204 -1

No files matched your search

@@ -35,6 +35,7 @@ import {
createCancellationBenchmarkSummary,
createCancellationIterationResult,
} from './m3u-refresh-cancellation-report';
import { assertPerformanceArtifactCapacity } from './performance-artifact-preflight';
import {
installMainCapture,
readMainCaptureStatus,
@@ -147,10 +148,11 @@ async function runIteration(
server: SyntheticServer
): Promise<CancellationIterationResult> {
const iterationDirectory = join(config.outputDirectory, definition.runId);
await assertPerformanceArtifactCapacity(iterationDirectory);
await mkdir(iterationDirectory, { recursive: false });
const dataDirectory = await mkdtemp(
join(tmpdir(), 'iptvnator-m3u-performance-')
);
await mkdir(iterationDirectory, { recursive: false });
let app: LaunchedElectronApp | null = null;
let rendererConsoleListener: ((message: ConsoleMessage) => void) | null =
null;
@@ -363,6 +365,7 @@ async function resolveConfiguration(): Promise<BenchmarkConfiguration> {
}
const outputDirectory = join(outputRoot, variant);
await assertMissing(outputDirectory);
await assertPerformanceArtifactCapacity(outputDirectory);
await mkdir(outputDirectory, { recursive: true });
return Object.freeze({
electronVersion: electronPackage.version,
@@ -0,0 +1,124 @@
/* eslint-disable playwright/expect-expect -- These are Node assertion-based performance contract tests. */
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import test from 'node:test';
import { dirname, resolve } from 'node:path';
import {
assertPerformanceArtifactCapacity,
MIN_PERFORMANCE_ARTIFACT_FREE_BYTES,
} from './performance-artifact-preflight';
test('checks the nearest existing ancestor using available blocks', async () => {
const requestedPath = resolve(
'/workspace/dist/performance/2026-07-26/baseline'
);
const checkedPaths: string[] = [];
const existingAncestor = dirname(dirname(requestedPath));
const result = await assertPerformanceArtifactCapacity(requestedPath, {
statfs: async (path) => {
checkedPaths.push(path);
if (path !== existingAncestor) {
throw Object.assign(new Error('missing'), { code: 'ENOENT' });
}
return {
bavail: 4_194_304n,
bsize: 1_024n,
};
},
});
assert.deepEqual(checkedPaths, [
requestedPath,
dirname(requestedPath),
existingAncestor,
]);
assert.deepEqual(result, {
availableBytes: 4_294_967_296n,
checkedPath: existingAncestor,
minimumBytes: MIN_PERFORMANCE_ARTIFACT_FREE_BYTES,
});
});
test('accepts exactly two GiB of available capacity', async () => {
const result = await assertPerformanceArtifactCapacity('/existing', {
statfs: async () => ({
bavail: 2_097_152n,
bsize: 1_024n,
}),
});
assert.equal(result.availableBytes, MIN_PERFORMANCE_ARTIFACT_FREE_BYTES);
});
test('fails before artifact creation when available capacity is below two GiB', async () => {
await assert.rejects(
assertPerformanceArtifactCapacity('/existing', {
statfs: async () => ({
bavail: 2_097_151n,
bsize: 1_024n,
}),
}),
/requires at least 2147483648 available bytes.*2147482624/
);
});
test('does not substitute free blocks that are unavailable to the process', async () => {
await assert.rejects(
assertPerformanceArtifactCapacity('/existing', {
statfs: async () => ({
bavail: 1n,
bfree: 9_999_999_999n,
bsize: 1_024n,
}),
}),
/performance-artifact-space-insufficient/
);
});
test('does not hide filesystem errors other than a missing path', async () => {
const denied = Object.assign(new Error('denied'), { code: 'EACCES' });
let calls = 0;
await assert.rejects(
assertPerformanceArtifactCapacity('/denied/child', {
statfs: async () => {
calls += 1;
throw denied;
},
}),
denied
);
assert.equal(calls, 1);
});
test('the benchmark preflights capacity before creating output directories', () => {
const source = readFileSync(
new URL('./m3u-refresh-cancellation.benchmark.ts', import.meta.url),
'utf8'
);
const runIteration = source.slice(
source.indexOf('async function runIteration('),
source.indexOf('async function seedPlaylist(')
);
const resolveConfiguration = source.slice(
source.indexOf('async function resolveConfiguration('),
source.indexOf('function readGitSourceState(')
);
for (const [name, section] of [
['iteration', runIteration],
['variant', resolveConfiguration],
] as const) {
const preflight = section.indexOf(
'await assertPerformanceArtifactCapacity('
);
const createDirectory = section.indexOf('await mkdir(');
assert.ok(preflight >= 0, `${name} preflight is missing`);
assert.ok(
preflight < createDirectory,
`${name} preflight must run before mkdir`
);
}
});
@@ -0,0 +1,76 @@
import { statfs } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
export const MIN_PERFORMANCE_ARTIFACT_FREE_BYTES =
2n * 1_024n * 1_024n * 1_024n;
interface FileSystemCapacity {
readonly bavail: bigint;
readonly bsize: bigint;
}
interface PerformanceArtifactPreflightDependencies {
readonly statfs?: (path: string) => Promise<FileSystemCapacity>;
}
export interface PerformanceArtifactCapacity {
readonly availableBytes: bigint;
readonly checkedPath: string;
readonly minimumBytes: bigint;
}
async function readFileSystemCapacity(
path: string
): Promise<FileSystemCapacity> {
const capacity = await statfs(path, { bigint: true });
return {
bavail: capacity.bavail,
bsize: capacity.bsize,
};
}
function isMissingPath(error: unknown): boolean {
return (
typeof error === 'object' &&
error !== null &&
'code' in error &&
error.code === 'ENOENT'
);
}
export async function assertPerformanceArtifactCapacity(
targetPath: string,
dependencies: PerformanceArtifactPreflightDependencies = {}
): Promise<PerformanceArtifactCapacity> {
const readCapacity = dependencies.statfs ?? readFileSystemCapacity;
let checkedPath = resolve(targetPath);
while (true) {
try {
const capacity = await readCapacity(checkedPath);
const availableBytes = capacity.bavail * capacity.bsize;
if (availableBytes < MIN_PERFORMANCE_ARTIFACT_FREE_BYTES) {
throw new Error(
`performance-artifact-space-insufficient: benchmark requires at least ${MIN_PERFORMANCE_ARTIFACT_FREE_BYTES} available bytes; ${availableBytes} available at ${checkedPath}`
);
}
return Object.freeze({
availableBytes,
checkedPath,
minimumBytes: MIN_PERFORMANCE_ARTIFACT_FREE_BYTES,
});
} catch (error) {
if (!isMissingPath(error)) {
throw error;
}
const parent = dirname(checkedPath);
if (parent === checkedPath) {
throw new Error(
`performance-artifact-space-unavailable: no existing ancestor for ${targetPath}`,
{ cause: error }
);
}
checkedPath = parent;
}
}
}