diff --git a/.dockerignore b/.dockerignore index 97285a9a1..db7aef61f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,6 +9,7 @@ !/tsconfig.json !/tsconfig.src.json !/entrypoint.sh +!/compute-heap.mjs # Ignore unnecessary files inside allowed directories # This should go after the allowed directories diff --git a/Dockerfile b/Dockerfile index f4720dec2..1f0974cad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,7 @@ COPY --from=builder --chown=docker:docker docs docs COPY --from=builder node_modules node_modules COPY --chown=docker:docker tsconfig.src.json tsconfig.json COPY entrypoint.sh entrypoint.sh +COPY compute-heap.mjs compute-heap.mjs RUN adduser -u 2004 -D docker &&\ chmod +x entrypoint.sh diff --git a/compute-heap.mjs b/compute-heap.mjs new file mode 100644 index 000000000..45473cde2 --- /dev/null +++ b/compute-heap.mjs @@ -0,0 +1,21 @@ +import os from "node:os"; + +const constrained = + typeof process.constrainedMemory === "function" + ? process.constrainedMemory() + : 0; +const limitBytes = constrained || os.totalmem(); +const limitMB = Math.floor(limitBytes / 1024 / 1024); + +// Reserve 25% headroom for non-V8-heap usage (native buffers, stack, code space, RSS overhead). +const budgetMB = Math.floor(limitMB * 0.75); + +// V8's new-space is TWO semi-spaces (from-space + to-space), so its worst-case +// footprint is 2x --max-semi-space-size. Size it as a fraction of the budget, +// clamped to V8's own sane range, then subtract it before sizing old-space so +// old-space + new-space never exceeds the budget. +const semiSpaceMB = Math.min(128, Math.max(16, Math.floor(budgetMB * 0.05))); +const newSpaceMB = semiSpaceMB * 2; +const oldSpaceMB = Math.max(1, budgetMB - newSpaceMB); + +process.stdout.write(`${oldSpaceMB} ${semiSpaceMB}`); diff --git a/entrypoint.sh b/entrypoint.sh index b269ddafd..cf54c93fd 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,6 +1,10 @@ #!/bin/sh +read -r HEAP_MB SEMI_MB <