chore(benchmarks): record the server require graph, and use it to attribute boot-time code
A flat module cache says 24 MiB of source is resident before the first request, but not
what pulled any of it in. Recording parent -> child as the CommonJS loader resolves it
answers that, and the answer redirects the remaining 'ship less server JS' work:
- Next loads all 74 app route bundles at startup, not lazily.
- One 16.10 MiB / 460-file subtree is reached by the tRPC/API routes AND by
instrumentation, so instrumentation's exclusive cost is 0 bytes - deferring it would
free nothing, because Next preloads /api/[...trpc] anyway.
- Only 1.10 MiB is common to every route, so this is the API surface pulling in every
integration, not a universal barrel.
This commit is contained in:
3 files changed
+170
-1
No files matched your search
@@ -105,6 +105,33 @@ globalThis.__homarrProbe = {
|
||||
moduleLoadList: () => process.moduleLoadList.slice(),
|
||||
};
|
||||
|
||||
/**
|
||||
* Opt-in recording of the CommonJS require graph.
|
||||
*
|
||||
* Knowing that 24 MiB of source is resident at boot does not say *why* any of it is there.
|
||||
* The module cache is a flat list with no parentage, so the only way to answer "what pulled
|
||||
* this chunk in" is to record parent → child as the loader resolves it. The preload runs
|
||||
* before Next, so the graph is complete from the entry point down.
|
||||
*
|
||||
* Enable with HOMARR_PROBE_TRACK_REQUIRES=1.
|
||||
*/
|
||||
if (process.env.HOMARR_PROBE_TRACK_REQUIRES === "1") {
|
||||
const edges = new Set();
|
||||
const originalLoad = Module._load;
|
||||
Module._load = function (request, parent, isMain) {
|
||||
const result = originalLoad.call(this, request, parent, isMain);
|
||||
try {
|
||||
const resolved = Module._resolveFilename(request, parent, isMain);
|
||||
// Builtins resolve to their bare name and carry no file cost, so they only add noise.
|
||||
if (resolved.includes("/")) edges.add(`${parent ? parent.filename : "(entry)"}\t${resolved}`);
|
||||
} catch {
|
||||
// An unresolvable request is the caller's problem, not the probe's.
|
||||
}
|
||||
return result;
|
||||
};
|
||||
globalThis.__homarrProbe.requireGraph = () => [...edges].map((edge) => edge.split("\t"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opt-in tracking of large Buffer allocations, by allocation site.
|
||||
*
|
||||
@@ -269,7 +296,9 @@ if (probePort > 0) {
|
||||
? JSON.stringify(probe.buffers ? probe.buffers() : { disabled: true })
|
||||
: request.url === "/fetches"
|
||||
? JSON.stringify(probe.fetches ? probe.fetches() : { disabled: true })
|
||||
: JSON.stringify({
|
||||
: request.url === "/requires"
|
||||
? JSON.stringify(probe.requireGraph ? probe.requireGraph() : { disabled: true })
|
||||
: JSON.stringify({
|
||||
usage: probe.usage(),
|
||||
spaces: probe.spaces(),
|
||||
heap: probe.heap(),
|
||||
|
||||
Reference in new issue
Block a user