mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 09:01:03 -08:00
fix(perf): reject incomplete renderer RSS samples
This commit is contained in:
1 parent
554eecfee0
commit
2fda6cea07
5 files changed
+136
-9
No files matched your search
@@ -1,4 +1,7 @@
|
||||
import type { RendererProcessRssCapture } from './renderer-process-rss-capture';
|
||||
import type {
|
||||
RendererProcessRssCapture,
|
||||
RendererProcessRssUnavailableReason,
|
||||
} from './renderer-process-rss-capture';
|
||||
|
||||
export const PERFORMANCE_ITERATION_KIND = {
|
||||
DIAGNOSTIC: 'diagnostic',
|
||||
@@ -254,6 +257,22 @@ export interface DatabaseWorkerPostGcValidity {
|
||||
readonly validMeasuredRunCount: number;
|
||||
}
|
||||
|
||||
export interface InvalidRendererRssMeasuredRun {
|
||||
readonly missingSampleCount: number;
|
||||
readonly reason:
|
||||
RendererProcessRssUnavailableReason | 'renderer-rss-capture-invalid';
|
||||
readonly runId: string;
|
||||
readonly validSampleCount: number;
|
||||
}
|
||||
|
||||
export interface RendererRssValidity {
|
||||
readonly invalidMeasuredRuns: readonly InvalidRendererRssMeasuredRun[];
|
||||
readonly measuredRunCount: number;
|
||||
readonly validForBenchmark: boolean;
|
||||
readonly validForComparison: boolean;
|
||||
readonly validMeasuredRunCount: number;
|
||||
}
|
||||
|
||||
export interface CancellationBenchmarkSummary {
|
||||
readonly cancellationEffectRate: number;
|
||||
readonly iterations: readonly CancellationIterationResult[];
|
||||
@@ -309,5 +328,6 @@ export interface CancellationBenchmarkSummary {
|
||||
};
|
||||
readonly validity: {
|
||||
readonly databaseWorkerPostGc: DatabaseWorkerPostGcValidity;
|
||||
readonly rendererRss: RendererRssValidity;
|
||||
};
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
NumericDistribution,
|
||||
PerformanceWorkerKind,
|
||||
RendererCaptureMetrics,
|
||||
RendererRssValidity,
|
||||
WorkerRequestPerformanceMetrics,
|
||||
} from './m3u-refresh-cancellation-contract';
|
||||
import {
|
||||
@@ -310,10 +311,55 @@ export function createCancellationBenchmarkSummary(
|
||||
validity: Object.freeze({
|
||||
databaseWorkerPostGc:
|
||||
assessDatabaseWorkerPostGcValidity(iterations),
|
||||
rendererRss: assessRendererRssValidity(iterations),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
function assessRendererRssValidity(
|
||||
iterations: readonly CancellationIterationResult[]
|
||||
): RendererRssValidity {
|
||||
const measured = iterations.filter(
|
||||
(iteration) => iteration.kind === PERFORMANCE_ITERATION_KIND.MEASURED
|
||||
);
|
||||
const invalidMeasuredRuns: RendererRssValidity['invalidMeasuredRuns'][number][] =
|
||||
[];
|
||||
let validMeasuredRunCount = 0;
|
||||
|
||||
for (const iteration of measured) {
|
||||
const rss = iteration.main.rendererWindow.rss;
|
||||
if (
|
||||
rss.identity !== null &&
|
||||
Number.isSafeInteger(rss.peakRssBytes) &&
|
||||
Number(rss.peakRssBytes) > 0 &&
|
||||
rss.unavailableReason === null &&
|
||||
rss.missingSampleCount === 0 &&
|
||||
Number.isSafeInteger(rss.validSampleCount) &&
|
||||
rss.validSampleCount > 0
|
||||
) {
|
||||
validMeasuredRunCount += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
invalidMeasuredRuns.push({
|
||||
missingSampleCount: rss.missingSampleCount,
|
||||
reason: rss.unavailableReason ?? 'renderer-rss-capture-invalid',
|
||||
runId: iteration.runId,
|
||||
validSampleCount: rss.validSampleCount,
|
||||
});
|
||||
}
|
||||
|
||||
const valid =
|
||||
measured.length > 0 && validMeasuredRunCount === measured.length;
|
||||
return Object.freeze({
|
||||
invalidMeasuredRuns: Object.freeze(invalidMeasuredRuns),
|
||||
measuredRunCount: measured.length,
|
||||
validForBenchmark: valid,
|
||||
validForComparison: valid,
|
||||
validMeasuredRunCount,
|
||||
});
|
||||
}
|
||||
|
||||
function derivePhases(
|
||||
main: MainCaptureMetrics,
|
||||
renderer: RendererCaptureMetrics
|
||||
|
||||
@@ -157,6 +157,13 @@ export async function runM3uRefreshCancellationBenchmark(): Promise<void> {
|
||||
`Cancellation effect was not observed in every measured run: ${summary.cancellationEffectRate}`
|
||||
);
|
||||
}
|
||||
if (!summary.validity.rendererRss.validForBenchmark) {
|
||||
throw new Error(
|
||||
`Renderer RSS capture is invalid: ${JSON.stringify(
|
||||
summary.validity.rendererRss.invalidMeasuredRuns
|
||||
)}`
|
||||
);
|
||||
}
|
||||
if (!summary.validity.databaseWorkerPostGc.validForBenchmark) {
|
||||
throw new Error(
|
||||
`Database worker post-GC capture is invalid: ${JSON.stringify(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* 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 {
|
||||
@@ -13,7 +14,8 @@ import type { RendererProcessRssCapture } from './renderer-process-rss-capture';
|
||||
function measuredIteration(
|
||||
rss: RendererProcessRssCapture,
|
||||
responsiveEvents: number,
|
||||
unresponsiveEvents: number
|
||||
unresponsiveEvents: number,
|
||||
runId: string
|
||||
): CancellationIterationResult {
|
||||
return {
|
||||
cancellationEffectObserved: true,
|
||||
@@ -51,7 +53,7 @@ function measuredIteration(
|
||||
longTasksMs: [],
|
||||
},
|
||||
},
|
||||
runId: 'measured',
|
||||
runId,
|
||||
} as unknown as CancellationIterationResult;
|
||||
}
|
||||
|
||||
@@ -71,19 +73,24 @@ test('summary uses only exact target-window RSS and scoped responsiveness events
|
||||
validSampleCount: 3,
|
||||
},
|
||||
1,
|
||||
2
|
||||
2,
|
||||
'run-01'
|
||||
),
|
||||
measuredIteration(
|
||||
{
|
||||
identity: null,
|
||||
identity: {
|
||||
creationTime: 1_721_234_567_890,
|
||||
pid: 43,
|
||||
},
|
||||
missingSampleCount: 1,
|
||||
peakRssBytes: null,
|
||||
unavailableReason:
|
||||
'renderer-process-metric-missing-at-start',
|
||||
validSampleCount: 0,
|
||||
'renderer-process-metric-missing-during-capture',
|
||||
validSampleCount: 2,
|
||||
},
|
||||
3,
|
||||
4
|
||||
4,
|
||||
'run-02'
|
||||
),
|
||||
]
|
||||
);
|
||||
@@ -99,4 +106,48 @@ test('summary uses only exact target-window RSS and scoped responsiveness events
|
||||
});
|
||||
assert.equal(summary.measured.responsiveEvents, 4);
|
||||
assert.equal(summary.measured.unresponsiveEvents, 6);
|
||||
assert.deepEqual(
|
||||
(
|
||||
summary as unknown as {
|
||||
readonly validity: {
|
||||
readonly rendererRss: unknown;
|
||||
};
|
||||
}
|
||||
).validity.rendererRss,
|
||||
{
|
||||
invalidMeasuredRuns: [
|
||||
{
|
||||
missingSampleCount: 1,
|
||||
reason: 'renderer-process-metric-missing-during-capture',
|
||||
runId: 'run-02',
|
||||
validSampleCount: 2,
|
||||
},
|
||||
],
|
||||
measuredRunCount: 2,
|
||||
validForBenchmark: false,
|
||||
validForComparison: false,
|
||||
validMeasuredRunCount: 1,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('benchmark preserves raw artifacts before rejecting incomplete renderer RSS', () => {
|
||||
const source = readFileSync(
|
||||
new URL('./m3u-refresh-cancellation.benchmark.ts', import.meta.url),
|
||||
'utf8'
|
||||
);
|
||||
const summaryWrite = source.indexOf(
|
||||
"writeJson(join(config.outputDirectory, 'summary.json'), summary)"
|
||||
);
|
||||
const validityGuard = source.indexOf(
|
||||
'summary.validity.rendererRss.validForBenchmark'
|
||||
);
|
||||
|
||||
assert.ok(summaryWrite >= 0, 'summary artifact write must exist');
|
||||
assert.ok(validityGuard >= 0, 'renderer RSS validity guard must exist');
|
||||
assert.ok(
|
||||
summaryWrite < validityGuard,
|
||||
'raw summary must be durable before the renderer RSS guard fails'
|
||||
);
|
||||
assert.match(source, /Renderer RSS capture is invalid/);
|
||||
});
|
||||
@@ -129,7 +129,10 @@ one exact `app.getAppMetrics()` PID and creation time, while responsive and
|
||||
unresponsive events come only from that window. Identity changes, missing or
|
||||
ambiguous process metrics, and invalid working-set values fail closed with a
|
||||
raw reason and nullable RSS; summaries exclude unavailable RSS instead of
|
||||
reporting zero. Each worker response retains a raw
|
||||
reporting zero. Formal runs also list every invalid measured capture under
|
||||
`summary.validity.rendererRss` and fail after persisting the raw summary unless
|
||||
all measured iterations contribute one valid exact-window RSS value. Each
|
||||
worker response retains a raw
|
||||
request-scoped record containing request/operation identity, received/work/flush
|
||||
timestamps, thread CPU, event-loop utilization, event-loop delay, and fixed
|
||||
unavailability or invalid reasons. Missing or malformed profiling metadata
|
||||
|
||||
Reference in new issue
Block a user