Files
MaashkinoozheandClaude Opus 5.5 896726a51b Performance trace, load test and chapter read-ahead
A switchable trace (DATA/perf/trace.on or PERF_TRACE=1) records every request's
phases (stat, open, first read, disk vs network wait), browser resource timings,
page-turn waits and flow stalls, a cold storage probe, event-loop lag, scans and
scraper calls as JSONL. tools/perf-report.js says which phase slow loads spend
their time in; tools/perf-bench.js simulates readers.

READ_AHEAD (default on) reads an opened chapter's pages, then the next chapter's,
into the OS cache so a NAS's cold reads stop reaching the browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 22:28:50 +01:00

4.9 KiB

Performance trace, load test and read-ahead

When pages load slowly, the trace shows where the time goes: the storage (a NAS, a disk waking up), the reader process (CPU, event loop), library scans running at the same time, or the network to the browser.

Switching the trace on and off

No restart is needed. The reader checks for a flag file every 5 seconds:

touch "$DATA/perf/trace.on"     # on   (Docker: the data folder mounted at /data)
rm "$DATA/perf/trace.on"        # off

PERF_TRACE=1 in the environment keeps it on from the start. While it is off, the reader does no extra work.

The trace goes to DATA/perf/trace-YYYY-MM-DD.jsonl (UTC days), one JSON object per line. Files older than 14 days are deleted; one day stops growing at 256 MB. The file contains request paths (manga and chapter names) and user names, so treat it like the rest of DATA.

What is recorded

Every line has t (server time, ms since the epoch) and k (kind):

k Fields Meaning
req m, path, status, ms, bytes, conn, aborted; for files stat, open, first, readEnd, disk, netWait; for pages also access One HTTP request. Phase fields are ms since the request arrived: stat file lookup done, open file opened, first first chunk read, readEnd last chunk read. disk = time spent waiting for the storage, netWait = time the file stream was paused because the client did not take data fast enough. conn > 1 means a reused keep-alive connection
client e: hello, res, view, stall Reported by the reader page while the trace is on (public/perf.js). res: the browser's resource timing of a page image (queue, dns, connect, ttfb, download, total, size, server = the reader's stat time, cached). view: a page turn in page/book mode, wait until the new page was decoded and painted, preloaded. stall: flow mode, a page on screen before its image, wait until it loaded. hello: browser, viewport, connection type
nas statMs, firstMs, ms, bytes or error Every PERF_NAS_PROBE_SECONDS (60) one random library page is read. Out of a large library it is almost never cached, so this is the storage's cold latency over time; seconds after a quiet period mean disks were in standby
loop lagP50, lagP99, lagMax, cpu, rss, heap Every 5 s: event-loop delay (ms), CPU % of one core, memory (MB)
scan mode, ms, changed Library scans (change checks every SCAN_INTERVAL, full reconciliations)
ahead tag, files, done, skipped, bytes, ms, replaced A read-ahead batch (below)
scraper endpoint, ms, status/error Calls from the reader to the scraper daemon
trace on The trace was switched on or off

Reading the result

node tools/perf-report.js "$DATA/perf"                          # all days
node tools/perf-report.js "$DATA/perf" --since 2026-10-29T18:00Z --slow 300
docker compose exec reader node tools/perf-report.js /data/perf  # inside the container

The report starts with a verdict (which phase dominates the slow loads), then percentiles for the server's phases, the browser's timings, page turns, flow stalls, the storage probe, the event loop, scans, read-ahead and the API routes. --json prints the same numbers for scripts and agents.

How to read it:

  • Slow firstRead / readRest, slow nas probes: the storage is slow. Probes of several seconds after minutes without reads point at disk standby (on a Synology: Control Panel → Hardware & Power → HDD Hibernation).
  • Slow stat: metadata lookups on the storage are slow (often during a scan, see scan).
  • High netWait, or browser download much longer than the server's ms: the network between server and browser (Wi-Fi, VPN, a proxy).
  • Browser queue: the browser waited for a free connection (too many parallel requests).
  • High lagMax: the reader process was busy and could not answer.

Load test

tools/perf-bench.js simulates readers paging through random chapters with a think time and a small look-ahead, and reports how long a reader looked at an empty page (wait), time to first byte and total per page. It only reads (no progress is written). Use a separate account:

BENCH_PASSWORD=… node tools/perf-bench.js --url http://server:8080 --user bench --password-env BENCH_PASSWORD \
  --readers 4 --chapters 3 --pages 40 --think 400 --ahead 2

Run it with the trace on to see the server's side of the same requests.

Read-ahead

READ_AHEAD=1 (default): when a chapter is opened, the reader reads its pages once in page order, starting at the saved page, two at a time, and then the next chapter's. The data is thrown away; the OS page cache keeps it, so the browser's requests are answered from memory instead of the storage. Opening another chapter replaces whatever is still queued. READ_AHEAD=0 turns it off (useful on a local SSD, or to compare with the trace).