mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-21 22:52:19 +03:00
fix(docker): warn when OMNIROUTE_MEMORY_MB disagrees with NODE_OPTIONS heap (#10818)
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
This commit is contained in:
@@ -49,6 +49,59 @@ export function envHasExplicitHeapFlag(env) {
|
||||
return String(sourceEnv?.NODE_OPTIONS || "").includes(MAX_OLD_SPACE_FLAG);
|
||||
}
|
||||
|
||||
/** Last `--max-old-space-size=` value in NODE_OPTIONS, or null if absent. */
|
||||
export function parseNodeOptionsHeapMb(nodeOptions) {
|
||||
const matches = [...String(nodeOptions || "").matchAll(/--max-old-space-size=(\d+)/g)];
|
||||
if (matches.length === 0) return null;
|
||||
const parsed = Number.parseInt(matches[matches.length - 1][1], 10);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when OMNIROUTE_MEMORY_MB is an explicit in-range integer (not the
|
||||
* unset/invalid fallback). Docker images set this; Compose may also set
|
||||
* NODE_OPTIONS — #10353 needs to know both knobs were intentionally present.
|
||||
*/
|
||||
export function envHasExplicitOmnirouteMemoryMb(env) {
|
||||
const sourceEnv = arguments.length === 0 ? process.env : env;
|
||||
const parsed = Number.parseInt(String(sourceEnv?.OMNIROUTE_MEMORY_MB ?? ""), 10);
|
||||
return Number.isFinite(parsed) && parsed >= 64 && parsed <= 16384;
|
||||
}
|
||||
|
||||
/**
|
||||
* Docker `run-standalone.mjs` appends `--max-old-space-size` from
|
||||
* OMNIROUTE_MEMORY_MB. V8 last-flag semantics mean that appended value wins
|
||||
* over an earlier NODE_OPTIONS heap. Warn once when both are set and disagree
|
||||
* so env dumps stop looking like NODE_OPTIONS is in effect (#10353).
|
||||
*
|
||||
* @returns {boolean} true when a warn was emitted
|
||||
*/
|
||||
export function warnConflictingHeapLimits(env, omnirouteMb, log = console.warn) {
|
||||
const nodeMb = parseNodeOptionsHeapMb(env?.NODE_OPTIONS);
|
||||
if (nodeMb == null || !envHasExplicitOmnirouteMemoryMb(env)) return false;
|
||||
if (nodeMb === omnirouteMb) return false;
|
||||
log(
|
||||
`[omniroute] heap limit conflict: OMNIROUTE_MEMORY_MB=${omnirouteMb} disagrees with NODE_OPTIONS --max-old-space-size=${nodeMb}. ` +
|
||||
`run-standalone.mjs / Docker appends OMNIROUTE_MEMORY_MB last, so the effective V8 heap is ${omnirouteMb} MB. ` +
|
||||
`Set only OMNIROUTE_MEMORY_MB (recommended) or make both values match.`
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* NODE_OPTIONS string for Docker / run-standalone.mjs.
|
||||
* Explicit OMNIROUTE_MEMORY_MB always appends (wins). Otherwise keep an
|
||||
* existing NODE_OPTIONS heap flag (#5238). Otherwise append the fallback.
|
||||
*/
|
||||
export function buildStandaloneNodeOptions(env = process.env, omnirouteMb) {
|
||||
const existing = String(env?.NODE_OPTIONS || "").trim();
|
||||
if (envHasExplicitOmnirouteMemoryMb(env)) {
|
||||
return `${existing} ${MAX_OLD_SPACE_FLAG}=${omnirouteMb}`.trim();
|
||||
}
|
||||
if (existing.includes(MAX_OLD_SPACE_FLAG)) return existing;
|
||||
return `${existing} ${MAX_OLD_SPACE_FLAG}=${omnirouteMb}`.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble the NODE_OPTIONS string for the spawned server, preserving any flags
|
||||
* the user already exported. #5238: `omniroute serve` used to UNCONDITIONALLY
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
resolveRuntimePorts,
|
||||
withRuntimePortEnv,
|
||||
resolveMaxOldSpaceMb,
|
||||
warnConflictingHeapLimits,
|
||||
buildStandaloneNodeOptions,
|
||||
spawnWithForwardedSignals,
|
||||
} from "../build/runtime-env.mjs";
|
||||
import { bootstrapEnv } from "../build/bootstrap-env.mjs";
|
||||
@@ -13,13 +15,13 @@ const env = bootstrapEnv();
|
||||
const runtimePorts = resolveRuntimePorts(env);
|
||||
const childEnv = withRuntimePortEnv(env, runtimePorts);
|
||||
|
||||
// #2939: honor OMNIROUTE_MEMORY_MB (default 512), the same knob
|
||||
// `omniroute serve` uses, so Docker users can control the server heap under
|
||||
// load / large SQLite DBs. A trailing --max-old-space-size wins, so this
|
||||
// overrides the image fallback without clobbering any other NODE_OPTIONS flags.
|
||||
// #2939 / #10353: OMNIROUTE_MEMORY_MB is the Docker/standalone heap knob.
|
||||
// When it is set, we append --max-old-space-size last (V8 last-flag wins).
|
||||
// When it is unset and NODE_OPTIONS already pins the heap, keep NODE_OPTIONS
|
||||
// (#5238). Warn when both are set and the numbers disagree.
|
||||
const maxOldSpaceMb = resolveMaxOldSpaceMb(childEnv.OMNIROUTE_MEMORY_MB);
|
||||
childEnv.NODE_OPTIONS =
|
||||
`${childEnv.NODE_OPTIONS || ""} --max-old-space-size=${maxOldSpaceMb}`.trim();
|
||||
warnConflictingHeapLimits(childEnv, maxOldSpaceMb);
|
||||
childEnv.NODE_OPTIONS = buildStandaloneNodeOptions(childEnv, maxOldSpaceMb);
|
||||
|
||||
// Prefer the WS-aware wrapper (server-ws.mjs) over the bare Next standalone
|
||||
// server.js: it installs the trusted peer-IP stamp (scripts/dev/peer-stamp.mjs)
|
||||
|
||||
Reference in New Issue
Block a user