0ef4c624c5
initial test from Ed's nuc:
metric pctl baseline current delta status
-----------------------------------------------------------------------------------------------
folder_cascade.list_depth1 p50 0.3ms 0.3ms -6.3% ok
folder_cascade.list_depth1 p95 2.3ms 0.5ms -75.7% ok
folder_cascade.list_depth1 p99 4.7ms 2.5ms -48.1% ok
folder_cascade.list_depth4 p50 0.4ms 0.3ms -10.0% ok
folder_cascade.list_depth4 p95 0.9ms 0.6ms -31.2% ok
folder_cascade.list_depth4 p99 2.4ms 1.0ms -56.7% ok
folder_cascade.list_depth8 p50 0.3ms 0.3ms -8.8% ok
folder_cascade.list_depth8 p95 0.6ms 0.5ms -22.2% ok
folder_cascade.list_depth8 p99 1.9ms 0.5ms -71.5% ok
folder_cascade.list_depth_deep p50 0.3ms 0.3ms -5.0% ok
folder_cascade.list_depth_deep p95 0.6ms 0.5ms -18.6% ok
folder_cascade.list_depth_deep p99 2.0ms 0.7ms -67.2% ok
share_cascade_rebac.list_grants p50 0.4ms 0.3ms -27.6% ok
share_cascade_rebac.list_grants p95 1.2ms 0.5ms -57.0% ok
share_cascade_rebac.list_grants p99 1.7ms 1.1ms -36.7% ok
share_cascade_rebac.fetch_as_grantee_depth1 p50 0.5ms 0.5ms -11.1% ok
share_cascade_rebac.fetch_as_grantee_depth1 p95 1.1ms 0.7ms -41.2% ok
share_cascade_rebac.fetch_as_grantee_depth1 p99 3.0ms 1.3ms -58.3% ok
share_cascade_rebac.fetch_as_grantee_depth4 p50 0.5ms 0.5ms -13.7% ok
share_cascade_rebac.fetch_as_grantee_depth4 p95 1.4ms 0.7ms -50.5% ok
share_cascade_rebac.fetch_as_grantee_depth4 p99 2.2ms 1.1ms -49.3% ok
share_cascade_rebac.fetch_as_grantee_depth8 p50 0.5ms 0.4ms -16.0% ok
share_cascade_rebac.fetch_as_grantee_depth8 p95 0.9ms 0.7ms -26.1% ok
share_cascade_rebac.fetch_as_grantee_depth8 p99 1.5ms 0.9ms -40.1% ok
share_cascade_rebac.fetch_as_grantee_depth_deep p50 0.5ms 0.4ms -17.3% ok
share_cascade_rebac.fetch_as_grantee_depth_deep p95 1.0ms 0.7ms -31.2% ok
share_cascade_rebac.fetch_as_grantee_depth_deep p99 1.6ms 0.8ms -49.4% ok
subject_group_nested.fetch_as_member_depth1 p50 0.5ms 0.4ms -8.4% ok
subject_group_nested.fetch_as_member_depth1 p95 0.6ms 0.6ms -10.2% ok
subject_group_nested.fetch_as_member_depth1 p99 1.4ms 0.6ms -55.2% ok
subject_group_nested.fetch_as_member_depth4 p50 0.5ms 0.5ms -7.9% ok
subject_group_nested.fetch_as_member_depth4 p95 0.6ms 0.6ms -4.0% ok
subject_group_nested.fetch_as_member_depth4 p99 0.7ms 0.6ms -2.2% ok
subject_group_nested.fetch_as_member_depth8 p50 0.5ms 0.4ms -8.9% ok
subject_group_nested.fetch_as_member_depth8 p95 0.5ms 0.6ms +7.1% ok
subject_group_nested.fetch_as_member_depth8 p99 0.6ms 0.7ms +10.3% ok
subject_group_nested.fetch_as_member_depth_deep p50 0.5ms 0.4ms -7.6% ok
subject_group_nested.fetch_as_member_depth_deep p95 0.6ms 0.5ms -11.9% ok
subject_group_nested.fetch_as_member_depth_deep p99 0.6ms 0.7ms +8.9% ok
73 lines
2.6 KiB
JavaScript
73 lines
2.6 KiB
JavaScript
#!/usr/bin/env node
|
|
// bake-baseline.mjs — convert the latest k6 --summary-export into the
|
|
// baseline shape that compare.mjs reads.
|
|
//
|
|
// Tolerance values from the existing baseline are preserved; only the p50/
|
|
// p95/p99 numbers are overwritten. Run this after `just load` to lock in a
|
|
// new accepted bar, then commit the result deliberately.
|
|
//
|
|
// Usage:
|
|
// node bake-baseline.mjs # auto-pick latest summary, target baseline/load.json
|
|
// node bake-baseline.mjs <summary> <baseline> # explicit paths (e.g. baseline/smoke.json)
|
|
|
|
import { readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';
|
|
import { join, dirname, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { argv, exit } from 'node:process';
|
|
|
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
|
|
function latestSummary() {
|
|
const dir = join(HERE, 'results');
|
|
const candidates = readdirSync(dir)
|
|
.filter((f) => f.startsWith('run-') && f.endsWith('.json'))
|
|
.map((f) => ({ f, mtime: statSync(join(dir, f)).mtimeMs }))
|
|
.sort((a, b) => b.mtime - a.mtime);
|
|
if (candidates.length === 0) {
|
|
console.error('bake-baseline: no run-*.json under tests/load/results/');
|
|
exit(2);
|
|
}
|
|
return join(dir, candidates[0].f);
|
|
}
|
|
|
|
const summaryPath = argv[2] ? resolve(argv[2]) : latestSummary();
|
|
const baselinePath = argv[3] ? resolve(argv[3]) : join(HERE, 'baseline/load.json');
|
|
|
|
const summary = JSON.parse(readFileSync(summaryPath, 'utf8'));
|
|
const baseline = JSON.parse(readFileSync(baselinePath, 'utf8'));
|
|
|
|
const metricKeyFor = (op) => `http_req_duration{op:${op}}`;
|
|
const PERCENTILES = [
|
|
{ baseline: 'p50', k6: 'med' },
|
|
{ baseline: 'p95', k6: 'p(95)' },
|
|
{ baseline: 'p99', k6: 'p(99)' },
|
|
];
|
|
|
|
let updated = 0;
|
|
let missing = 0;
|
|
|
|
for (const [op, entry] of Object.entries(baseline)) {
|
|
if (op.startsWith('_')) continue;
|
|
const metric = summary.metrics?.[metricKeyFor(op)];
|
|
if (!metric) {
|
|
console.warn(` missing: ${op} (no data in ${summaryPath})`);
|
|
missing++;
|
|
continue;
|
|
}
|
|
// k6 --summary-export puts percentile values directly on the metric object
|
|
// (alongside `thresholds`), not inside a `.values` wrapper.
|
|
let touched = false;
|
|
for (const p of PERCENTILES) {
|
|
const cur = metric[p.k6];
|
|
if (cur === undefined) continue;
|
|
entry[p.baseline] = Number(cur.toFixed(2));
|
|
touched = true;
|
|
}
|
|
if (touched) updated++;
|
|
}
|
|
|
|
writeFileSync(baselinePath, `${JSON.stringify(baseline, null, 2)}\n`);
|
|
console.log(`bake-baseline: updated ${updated} metric(s), missing ${missing} in ${baselinePath}`);
|
|
console.log(`Source: ${summaryPath}`);
|
|
console.log('Commit deliberately: chore(load): accept new baseline for <reason>');
|