Add Prettier and ESLint tooling (Refs #77)

This commit is contained in:
2026-10-09 15:56:13 -04:00
parent 8ed059b8d3
commit 16469e2d43
711 changed files with 99737 additions and 28286 deletions
+238 -69
View File
@@ -24,33 +24,51 @@ const allowed = new Set(['--scenario', '--runs', '--baseline-ref', '--json']);
for (let index = 0; index < arguments_.length; index += 2) {
const key = arguments_[index];
const value = arguments_[index + 1];
if (!allowed.has(key) || !value) throw new Error('Usage: --scenario small|large|all --runs N --baseline-ref COMMIT --json PATH');
if (!allowed.has(key) || !value)
throw new Error(
'Usage: --scenario small|large|all --runs N --baseline-ref COMMIT --json PATH'
);
options[key.slice(2)] = value;
}
options.runs = Number(options.runs);
if (!Number.isInteger(options.runs) || options.runs < 1 || options.runs > 20) throw new Error('--runs must be an integer from 1 to 20');
if (!['small', 'large', 'all'].includes(options.scenario)) throw new Error('--scenario must be small, large, or all');
if (!Number.isInteger(options.runs) || options.runs < 1 || options.runs > 20)
throw new Error('--runs must be an integer from 1 to 20');
if (!['small', 'large', 'all'].includes(options.scenario))
throw new Error('--scenario must be small, large, or all');
const baseline = options['baseline-ref']
? execFileSync('git', ['rev-parse', '--verify', '--end-of-options', `${options['baseline-ref']}^{commit}`], { cwd: repo, encoding: 'utf8' }).trim()
? execFileSync(
'git',
['rev-parse', '--verify', '--end-of-options', `${options['baseline-ref']}^{commit}`],
{ cwd: repo, encoding: 'utf8' }
).trim()
: null;
const definitions = [
{ name: 'small', matches: 2000, players: 500 },
{ name: 'large', matches: 50000, players: 10000 }
].filter(item => options.scenario === 'all' || item.name === options.scenario);
].filter((item) => options.scenario === 'all' || item.name === options.scenario);
const temporary = mkdtempSync(join(tmpdir(), 'cherry-leaderboard-benchmark-'));
const source = (file, ref) => ref
? execFileSync('git', ['show', `${ref}:${file}`], { cwd: repo, encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 })
: readFileSync(join(repo, file), 'utf8');
const percentile = (values, fraction) => [...values].sort((a, b) => a - b)[Math.ceil(values.length * fraction) - 1];
const source = (file, ref) =>
ref
? execFileSync('git', ['show', `${ref}:${file}`], {
cwd: repo,
encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024
})
: readFileSync(join(repo, file), 'utf8');
const percentile = (values, fraction) =>
[...values].sort((a, b) => a - b)[Math.ceil(values.length * fraction) - 1];
function seed(handle, definition) {
handle.exec(source('src/lib/server/db/schema.sql'));
handle.exec("CREATE TABLE report_revisions(topic TEXT PRIMARY KEY, revision INTEGER NOT NULL); INSERT INTO report_revisions VALUES ('leaderboard',0)");
handle.exec(
"CREATE TABLE report_revisions(topic TEXT PRIMARY KEY, revision INTEGER NOT NULL); INSERT INTO report_revisions VALUES ('leaderboard',0)"
);
const match = handle.prepare(`INSERT INTO leaderboard_matches
(id, server, game, map, map_key, map_name, started_at, ended_at, last_received_at,
status, start_observed, start_players, highest_round, last_event, last_fingerprint)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 4, ?, ?, ?)`);
const identity = handle.prepare(`INSERT INTO leaderboard_players (game, guid, name, first_seen_at, last_seen_at)
const identity =
handle.prepare(`INSERT INTO leaderboard_players (game, guid, name, first_seen_at, last_seen_at)
VALUES (?, ?, ?, ?, ?) ON CONFLICT(game, guid) DO UPDATE SET
name = CASE WHEN excluded.last_seen_at >= last_seen_at THEN excluded.name ELSE name END,
last_seen_at = MAX(last_seen_at, excluded.last_seen_at)`);
@@ -60,19 +78,38 @@ function seed(handle, definition) {
first_observed_round, present_at_start)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
let state = 20260918;
const random = () => { state = (Math.imul(state, 1664525) + 1013904223) >>> 0; return state / 4294967296; };
const random = () => {
state = (Math.imul(state, 1664525) + 1013904223) >>> 0;
return state / 4294967296;
};
handle.transaction(() => {
for (let id = 1; id <= definition.matches; id++) {
const mapIndex = Math.floor(random() * 12);
const game = mapIndex < 6 ? 't5' : 't6';
const map = `map_${mapIndex}`;
const status = id % 20 === 0 ? 'active' : id % 20 === 1 ? 'interrupted' : 'completed';
const started = new Date(Date.UTC(2023, 0, 1) + Math.floor(id / definition.matches * 3 * 365 * 86400000)).toISOString();
const ended = new Date(Date.parse(started) + (20 + Math.floor(random() * 130)) * 60000).toISOString();
const started = new Date(
Date.UTC(2023, 0, 1) + Math.floor((id / definition.matches) * 3 * 365 * 86400000)
).toISOString();
const ended = new Date(
Date.parse(started) + (20 + Math.floor(random() * 130)) * 60000
).toISOString();
const round = id % 101 === 0 ? 1 : 2 + Math.floor(random() * 60);
match.run(id, `server_${id % 24}`, game, map, `${game}:${map}`, `Map ${mapIndex}`,
started, status === 'completed' ? ended : null, ended, status, round,
status === 'completed' ? 'game_end' : 'heartbeat', `event_${id}`);
match.run(
id,
`server_${id % 24}`,
game,
map,
`${game}:${map}`,
`Map ${mapIndex}`,
started,
status === 'completed' ? ended : null,
ended,
status,
round,
status === 'completed' ? 'game_end' : 'heartbeat',
`event_${id}`
);
const roster = new Set();
while (roster.size < 4) roster.add(Math.floor(random() ** 2 * definition.players));
// Reserve one completed participation for an explicitly ineligible player.
@@ -84,16 +121,47 @@ function seed(handle, definition) {
const kills = Math.floor(round * 18 + random() * 800);
const presentAtStart = random() > 0.1;
const firstRound = presentAtStart ? 0 : Math.max(1, Math.floor(random() * round));
participation.run(id, guid, round, Math.max(0, round - Math.max(1, firstRound)), kills,
Math.floor(kills * random()), Math.floor(random() * 8), Math.floor(random() * 12),
Math.floor(random() * 100000), Math.max(0, round - 1), Number(status === 'active'),
Number(random() > 0.01), Number(status === 'completed' && random() > 0.15),
started, ended, firstRound, Number(presentAtStart));
participation.run(
id,
guid,
round,
Math.max(0, round - Math.max(1, firstRound)),
kills,
Math.floor(kills * random()),
Math.floor(random() * 8),
Math.floor(random() * 12),
Math.floor(random() * 100000),
Math.max(0, round - 1),
Number(status === 'active'),
Number(random() > 0.01),
Number(status === 'completed' && random() > 0.15),
started,
ended,
firstRound,
Number(presentAtStart)
);
}
if (id === definition.matches - 2) {
identity.run(game, 'benchmark-low-sample', 'Low sample', started, ended);
participation.run(id, 'benchmark-low-sample', 2, 1, 20, 5, 0, 0, 1000,
1, 0, 1, 1, started, ended, 0, 1);
participation.run(
id,
'benchmark-low-sample',
2,
1,
20,
5,
0,
0,
1000,
1,
0,
1,
1,
started,
ended,
0,
1
);
}
}
})();
@@ -104,15 +172,27 @@ function loadImplementation(handle, ref) {
const modules = new Map();
const hashes = {};
let recorded = null;
const database = { prepare(sql) {
const statement = handle.prepare(sql);
return Object.fromEntries(['all', 'get'].map(method => [method, (...parameters) => {
const start = performance.now();
const result = statement[method](...parameters);
if (recorded) recorded.push({ sql, parameters, milliseconds: performance.now() - start });
return result;
}]));
} };
const database = {
prepare(sql) {
const statement = handle.prepare(sql);
return Object.fromEntries(
['all', 'get'].map((method) => [
method,
(...parameters) => {
const start = performance.now();
const result = statement[method](...parameters);
if (recorded)
recorded.push({
sql,
parameters,
milliseconds: performance.now() - start
});
return result;
}
])
);
}
};
function load(file) {
if (file === 'src/lib/server/db/index.ts') return { db: () => database };
if (modules.has(file)) return modules.get(file);
@@ -123,10 +203,14 @@ function loadImplementation(handle, ref) {
}).outputText;
const exports = {};
modules.set(file, exports);
const localRequire = specifier => {
const localRequire = (specifier) => {
if (!specifier.startsWith('.')) return require(specifier);
const target = relative(repo, resolve(repo, dirname(file), specifier)).replace(/\.js$/, '.ts');
if (!target.startsWith('src/lib/')) throw new Error(`Unexpected benchmark dependency: ${target}`);
const target = relative(repo, resolve(repo, dirname(file), specifier)).replace(
/\.js$/,
'.ts'
);
if (!target.startsWith('src/lib/'))
throw new Error(`Unexpected benchmark dependency: ${target}`);
return load(target);
};
new Function('require', 'exports', compiled)(localRequire, exports);
@@ -134,10 +218,14 @@ function loadImplementation(handle, ref) {
}
const api = load('src/lib/server/fleet/leaderboard.ts');
return {
name: ref ? 'baseline' : 'current', ref, hashes,
metrics: load('src/lib/leaderboard.ts').leaderboardMetrics.map(metric => metric.key),
name: ref ? 'baseline' : 'current',
ref,
hashes,
metrics: load('src/lib/leaderboard.ts').leaderboardMetrics.map((metric) => metric.key),
run(test) {
return test.guid ? api.getPlayerLeaderboardEntry(test.guid, test.options) : api.getLeaderboard(test.options);
return test.guid
? api.getPlayerLeaderboardEntry(test.guid, test.options)
: api.getLeaderboard(test.options);
},
measure(test) {
recorded = [];
@@ -152,8 +240,16 @@ function loadImplementation(handle, ref) {
}
const report = {
environment: { node: process.version, sqlite: null, platform: platform(), arch: arch(), cpu: cpus()[0]?.model },
baselineRef: baseline, runs: options.runs, seed: 20260918,
environment: {
node: process.version,
sqlite: null,
platform: platform(),
arch: arch(),
cpu: cpus()[0]?.model
},
baselineRef: baseline,
runs: options.runs,
seed: 20260918,
notes: 'Synthetic file-backed SQLite, one warmup per case, rotating execution order, warm OS cache. Includes SQL preparation and source-record lookup; excludes HTTP and browser rendering. Shared local CPU; timings are not production latency. Schema and indexes are current for both implementations.',
scenarios: []
};
@@ -161,77 +257,148 @@ try {
for (const definition of definitions) {
const handle = new Database(join(temporary, `${definition.name}.sqlite`));
try {
report.environment.sqlite = handle.prepare('SELECT sqlite_version() AS version').get().version;
report.environment.sqlite = handle
.prepare('SELECT sqlite_version() AS version')
.get().version;
seed(handle, definition);
const implementations = [...(baseline ? [loadImplementation(handle, baseline)] : []), loadImplementation(handle)];
const implementations = [
...(baseline ? [loadImplementation(handle, baseline)] : []),
loadImplementation(handle)
];
const cases = [
{ name: 'unfiltered', options: {} },
{ name: 'kills', options: { metric: 'kills' } },
{ name: 'game', options: { game: 't5', metric: 'kills' } },
{ name: 'map', options: { game: 't6', map: 't6:map_8', metric: 'kills' } },
{ name: 'search', options: { q: '00042', metric: 'kills' } },
{ name: 'deep-page', options: { metric: 'kills', offset: Math.floor(definition.players / 2) } },
{
name: 'deep-page',
options: { metric: 'kills', offset: Math.floor(definition.players / 2) }
},
{ name: 'efficiency', options: { metric: 'headshotRate' } },
{ name: 'exact-player', guid: '10000000000000042', options: { metric: 'kills' } },
{ name: 'empty-search', options: { q: 'No such player exists' } },
{ name: 'past-end', options: { offset: 999999 } },
{ name: 'empty-map', options: { map: 't5:missing' } }
];
const counts = handle.prepare(`SELECT COUNT(*) AS participationRows,
COUNT(DISTINCT guid) AS uniquePlayers FROM leaderboard_match_players`).get();
const scenario = { ...definition, ...counts, maps: 12, servers: 24, completedShare: 0.9, results: [] };
const counts = handle
.prepare(
`SELECT COUNT(*) AS participationRows,
COUNT(DISTINCT guid) AS uniquePlayers FROM leaderboard_match_players`
)
.get();
const scenario = {
...definition,
...counts,
maps: 12,
servers: 24,
completedShare: 0.9,
results: []
};
report.scenarios.push(scenario);
// Broad parity is separate from timings so every ratio and edge case is
// checked without multiplying the larger fixture's timed request matrix.
if (baseline && definition.name === 'small') {
const scopes = [{}, { game: 't5' }, { game: 't6', map: 't6:map_8' }, { q: '00042' },
{ q: 'No such player exists' }, { offset: 999999 }, { map: 't5:missing' }];
const scopes = [
{},
{ game: 't5' },
{ game: 't6', map: 't6:map_8' },
{ q: '00042' },
{ q: 'No such player exists' },
{ offset: 999999 },
{ map: 't5:missing' }
];
let checked = 0;
for (const metric of implementations[1].metrics) {
for (const scope of scopes) {
const test = { options: { ...scope, metric } };
assert.deepStrictEqual(implementations[1].run(test), implementations[0].run(test),
`${definition.name}/${metric}/${JSON.stringify(scope)}: full-response parity`);
assert.deepStrictEqual(
implementations[1].run(test),
implementations[0].run(test),
`${definition.name}/${metric}/${JSON.stringify(scope)}: full-response parity`
);
checked++;
}
for (const guid of ['10000000000000042', 'benchmark-low-sample', 'benchmark-missing']) {
for (const guid of [
'10000000000000042',
'benchmark-low-sample',
'benchmark-missing'
]) {
const test = { guid, options: { metric } };
assert.deepStrictEqual(implementations[1].run(test), implementations[0].run(test),
`${definition.name}/${metric}/${guid}: exact-player parity`);
assert.deepStrictEqual(
implementations[1].run(test),
implementations[0].run(test),
`${definition.name}/${metric}/${guid}: exact-player parity`
);
checked++;
}
}
scenario.parityCases = checked;
console.log(JSON.stringify({ scenario: definition.name, parityCases: checked, parity: true }));
console.log(
JSON.stringify({
scenario: definition.name,
parityCases: checked,
parity: true
})
);
}
for (const test of cases) {
const expected = implementations[0].run(test);
for (const implementation of implementations.slice(1)) {
assert.deepStrictEqual(implementation.run(test), expected, `${definition.name}/${test.name}: baseline parity`);
assert.deepStrictEqual(
implementation.run(test),
expected,
`${definition.name}/${test.name}: baseline parity`
);
}
const samples = Object.fromEntries(implementations.map(item => [item.name, []]));
const samples = Object.fromEntries(implementations.map((item) => [item.name, []]));
const latestStatements = {};
for (let index = 0; index < options.runs; index++) {
const rotation = index % implementations.length;
for (const implementation of [...implementations.slice(rotation), ...implementations.slice(0, rotation)]) {
for (const implementation of [
...implementations.slice(rotation),
...implementations.slice(0, rotation)
]) {
const measured = implementation.measure(test);
samples[implementation.name].push(measured.milliseconds);
latestStatements[implementation.name] = measured.statements;
}
}
const result = { ...test, parity: baseline ? true : null, implementations: implementations.map(implementation => ({
name: implementation.name, sourceHashes: implementation.hashes,
medianMs: percentile(samples[implementation.name], 0.5),
p95Ms: percentile(samples[implementation.name], 0.95), samplesMs: samples[implementation.name],
statements: latestStatements[implementation.name].map(statement => ({ ...statement,
plan: handle.prepare(`EXPLAIN QUERY PLAN ${statement.sql}`).all(...statement.parameters)
const result = {
...test,
parity: baseline ? true : null,
implementations: implementations.map((implementation) => ({
name: implementation.name,
sourceHashes: implementation.hashes,
medianMs: percentile(samples[implementation.name], 0.5),
p95Ms: percentile(samples[implementation.name], 0.95),
samplesMs: samples[implementation.name],
statements: latestStatements[implementation.name].map((statement) => ({
...statement,
plan: handle
.prepare(`EXPLAIN QUERY PLAN ${statement.sql}`)
.all(...statement.parameters)
}))
}))
})) };
};
scenario.results.push(result);
console.log(JSON.stringify({ scenario: definition.name, case: test.name, parity: result.parity,
medianMs: Object.fromEntries(result.implementations.map(item => [item.name, Math.round(item.medianMs)])) }));
console.log(
JSON.stringify({
scenario: definition.name,
case: test.name,
parity: result.parity,
medianMs: Object.fromEntries(
result.implementations.map((item) => [
item.name,
Math.round(item.medianMs)
])
)
})
);
}
} finally { handle.close(); }
} finally {
handle.close();
}
}
if (options.json) {
const target = resolve(options.json);
@@ -239,4 +406,6 @@ try {
writeFileSync(target, JSON.stringify(report, null, 2));
console.log(`Detailed results: ${target}`);
}
} finally { rmSync(temporary, { recursive: true, force: true }); }
} finally {
rmSync(temporary, { recursive: true, force: true });
}