mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-07-20 20:01:21 +00:00
Some checks failed
ship-ci / ci (push) Has been cancelled
Host enablement for the plugin ES-module migration: sandboxed /api/plugins/{id}/src/ serving, no-cache+weak-ETag/304 live-edit caching on src/+screen.js+assets, scriptType:module loader injection + scriptType/minHost manifest passthrough; constitution v1.2.0 + module playbook + signed size-exemptions register + maintainer/CI-only ESLint gate; rerunnable perf-baseline harness. Reviewed by Codex (local), Copilot, and CodeRabbit.
95 lines
4.3 KiB
JavaScript
95 lines
4.3 KiB
JavaScript
// Perf-baseline harness for the module-migration refactor (R0).
|
|
//
|
|
// Rerun this after every phase (R0 → R3c) to prove the split does not regress
|
|
// screen-entry, frame-time, memory, or server latency. It writes a markdown
|
|
// results block to stdout; paste it into docs/perf-baseline.md (or redirect).
|
|
//
|
|
// Usage:
|
|
// node scripts/perf-baseline.mjs --base http://127.0.0.1:8000 [--n 60] [--soak 30]
|
|
//
|
|
// Maintainer/CI-only dev tooling (uses the committed @playwright/test browser);
|
|
// never part of the serve or Docker path. Metrics that need a seeded library
|
|
// with charts (playback frame-time, screen-entry into a live highway) are
|
|
// clearly labelled — run those against an environment with real songs.
|
|
|
|
import { createRequire } from 'module';
|
|
const require = createRequire(import.meta.url);
|
|
const { chromium } = require('@playwright/test');
|
|
|
|
const args = new Map();
|
|
for (let i = 2; i < process.argv.length; i += 2) args.set(process.argv[i].replace(/^--/, ''), process.argv[i + 1]);
|
|
const BASE = args.get('base') || 'http://127.0.0.1:8000';
|
|
const N = parseInt(args.get('n') || '60', 10);
|
|
const SOAK_S = parseInt(args.get('soak') || '30', 10);
|
|
|
|
const pct = (xs, p) => {
|
|
if (!xs.length) return null;
|
|
const s = [...xs].sort((a, b) => a - b);
|
|
return s[Math.min(s.length - 1, Math.floor((p / 100) * s.length))];
|
|
};
|
|
const ms = (x) => (x == null ? '—' : `${x.toFixed(1)}`);
|
|
|
|
// ── Server latency: p50/p95/p99 over N requests per endpoint ──────────────────
|
|
async function serverLatency(paths) {
|
|
const rows = [];
|
|
for (const path of paths) {
|
|
const t = [];
|
|
let status = 0;
|
|
for (let i = 0; i < N; i++) {
|
|
const t0 = performance.now();
|
|
try {
|
|
const r = await fetch(BASE + path);
|
|
status = r.status;
|
|
await r.arrayBuffer();
|
|
} catch { status = -1; }
|
|
t.push(performance.now() - t0);
|
|
}
|
|
rows.push({ path, status, p50: pct(t, 50), p95: pct(t, 95), p99: pct(t, 99) });
|
|
}
|
|
return rows;
|
|
}
|
|
|
|
// ── Client: cold boot-to-interactive + idle memory after a soak ───────────────
|
|
async function clientMetrics() {
|
|
const browser = await chromium.launch();
|
|
const page = await browser.newPage();
|
|
const t0 = Date.now();
|
|
await page.goto(BASE, { waitUntil: 'networkidle', timeout: 60000 });
|
|
const bootMs = Date.now() - t0;
|
|
|
|
// performance.memory is Chromium-only; JS heap after settle.
|
|
const mem0 = await page.evaluate(() => (performance.memory ? performance.memory.usedJSHeapSize : null));
|
|
await page.waitForTimeout(SOAK_S * 1000);
|
|
const mem1 = await page.evaluate(() => (performance.memory ? performance.memory.usedJSHeapSize : null));
|
|
|
|
const scripts = await page.evaluate(() =>
|
|
document.querySelectorAll('script[data-plugin-id]').length);
|
|
|
|
await browser.close();
|
|
return { bootMs, memStartMB: mem0 && mem0 / 1048576, memSoakMB: mem1 && mem1 / 1048576, scripts };
|
|
}
|
|
|
|
const server = await serverLatency([
|
|
'/api/version',
|
|
'/api/plugins',
|
|
'/api/library?limit=60',
|
|
'/api/library/artists',
|
|
]);
|
|
const client = await clientMetrics();
|
|
|
|
const now = new Date().toISOString();
|
|
let out = `\n<!-- generated by scripts/perf-baseline.mjs @ ${now} against ${BASE} (n=${N}, soak=${SOAK_S}s) -->\n\n`;
|
|
out += `### Server latency (ms)\n\n| Endpoint | status | p50 | p95 | p99 |\n|---|---|---|---|---|\n`;
|
|
for (const r of server) out += `| \`${r.path}\` | ${r.status} | ${ms(r.p50)} | ${ms(r.p95)} | ${ms(r.p99)} |\n`;
|
|
out += `\n### Client\n\n| Metric | Value |\n|---|---|\n`;
|
|
out += `| Cold boot → networkidle | ${client.bootMs} ms |\n`;
|
|
out += `| JS heap after load | ${client.memStartMB ? client.memStartMB.toFixed(1) + ' MB' : '—'} |\n`;
|
|
out += `| JS heap after ${SOAK_S}s idle soak | ${client.memSoakMB ? client.memSoakMB.toFixed(1) + ' MB' : '—'} |\n`;
|
|
out += `| Plugin scripts injected | ${client.scripts} |\n`;
|
|
out += `\n> **Requires a seeded library** (not captured by this run): playback frame-time p95\n`;
|
|
out += `> on the 2D + 3D highway, and screen-entry (plugin inject → interactive) for\n`;
|
|
out += `> editor / notedetect / highway_3d with a real chart loaded. Run this harness\n`;
|
|
out += `> against an environment with charts in \`DLC_DIR\` to fill those in.\n`;
|
|
|
|
console.log(out);
|