Read-ahead per reader; compose passes READ_AHEAD and PERF_TRACE; report fixes

Each user gets an own read-ahead queue, served in turn, so two people reading
at once no longer cancel each other's chapter. The report rounds phase times
and measures the quiet time before a slow storage probe correctly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MaashkinoozheandClaude Opus 5.5 committed 2026-10-29 22:51:19 +01:00
1 parent 896726a51b
commit d7d3bf5b2b
6 files changed
+57 -34

No files matched your search

+6 -5
View File
@@ -53,7 +53,8 @@ function serverPhases(e) {
const stat = e.stat ?? 0, open = e.open != null ? e.open - stat : 0, firstRead = e.first != null && e.open != null ? e.first - e.open : 0;
const rest = Math.max(0, (e.disk ?? 0) - firstRead), net = e.netWait ?? 0;
const tail = e.readEnd != null ? Math.max(0, e.ms - e.readEnd) : 0;
return { stat, open, firstRead, readRest: rest, netWait: net, tail };
const r = v => Math.round(v * 10) / 10;
return { stat: r(stat), open: r(open), firstRead: r(firstRead), readRest: r(rest), netWait: r(net), tail: r(tail) };
}
const dominant = phases => Object.entries(phases).sort((a, b) => b[1] - a[1])[0][0];
@@ -106,12 +107,12 @@ function analyse(events, { slow }) {
// Storage probe: cold reads of random pages; a long read after quiet time points at disks waking up.
const nas = events.filter(e => e.k === 'nas' && !e.error);
let lastIo = null;
const ioTimes = events.filter(e => isPageImage(e) || e.k === 'nas' || e.k === 'ahead').map(e => e.t);
// Library reads that finished before a probe started (a probe's own read does not count).
const ioEnds = events.filter(e => isPageImage(e) || e.k === 'nas' || e.k === 'ahead').map(e => e.t);
out.nas = { probes: nas.length, errors: events.filter(e => e.k === 'nas' && e.error).length, first: stats(nas.map(e => e.firstMs)), total: stats(nas.map(e => e.ms)) };
out.nas.slow = nas.filter(e => e.firstMs >= 1000).map(e => {
let i = ioTimes.findIndex(t => t >= e.t); lastIo = i > 0 ? ioTimes[i - 1] : null;
return { at: new Date(e.t).toISOString(), firstMs: e.firstMs, quietMinBefore: lastIo ? round((e.t - e.ms - lastIo) / 60000) : null };
const start = e.t - e.ms, before = ioEnds.filter(t => t < start).at(-1);
return { at: new Date(e.t).toISOString(), firstMs: e.firstMs, quietMinBefore: before ? round((start - before) / 60000) : null };
}).slice(0, 30);
const loops = events.filter(e => e.k === 'loop');