Public: anyone can read this; the crew can change it.
A small pod keeps room for the forge: a cell gets the pod's memory less the forge's own #12354
Check · Review · Land · Live done — Landed and live in production.
torque/forge:torque/12346-a-small-pod-keeps-room-for-the-forge-a-c → main 1 commit fork
2 files
changed +22
−2
src/apps/contain.ts +15 −2
| @@ -122,6 +122,19 @@ export const POD_MEMORY_FILE = "/sys/fs/cgroup/memory.max"; | ||
| 122 | 122 | /** The share of the pod's memory a single cell may be given. */ |
| 123 | 123 | export const POD_MEMORY_SHARE = 0.75; |
| 124 | 124 | |
| 125 | /** | |
| 126 | * What the forge itself needs inside its pod, MiB, measured at about 170 at | |
| 127 | * rest and 200 under a first hour's work (#12092): a cell is never given the | |
| 128 | * pod's memory down to this, so on a small machine the check dies, not the forge. | |
| 129 | */ | |
| 130 | export const FORGE_OWN_MIB = 192; | |
| 131 | ||
| 132 | /** A share of the pod's bytes for one cell, never more than the pod less the forge's own room. */ | |
| 133 | export function podShareMiB(bytes: number): number { | |
| 134 | const mib = bytes / 1048576; | |
| 135 | return Math.max(1, Math.floor(Math.min(mib * POD_MEMORY_SHARE, mib - FORGE_OWN_MIB))); | |
| 136 | } | |
| 137 | ||
| 125 | 138 | /** |
| 126 | 139 | * The most memory one cell gets, MiB: `FORGE_CELL_MEMORY_MAX_MIB` when set, |
| 127 | 140 | * else a share of the pod's own `memory.max`, else 4096 where the host |
| @@ -165,7 +178,7 @@ function machineCeiling( | ||
| 165 | 178 | try { |
| 166 | 179 | const bytes = Number(readFileSync(podFile, "utf8").trim()); |
| 167 | 180 | if (Number.isFinite(bytes) && bytes > 0) |
| 168 | return { mib: Math.max(1, Math.floor((bytes * POD_MEMORY_SHARE) / 1048576)), from: "pod" }; | |
| 181 | return { mib: podShareMiB(bytes), from: "pod" }; | |
| 169 | 182 | } catch { |
| 170 | 183 | // no cgroup v2 here, or "max" (unlimited): the fixed default below |
| 171 | 184 | } |
| @@ -191,7 +204,7 @@ function ceilingText(from: CeilingFrom | undefined): string { | ||
| 191 | 204 | : from === "size" |
| 192 | 205 | ? `${OWN_SIZE_SHARE * 100}% of this child forge's own size` |
| 193 | 206 | : from === "pod" |
| 194 | ? `${POD_MEMORY_SHARE * 100}% of the pod's memory.max` | |
| 207 | ? `${POD_MEMORY_SHARE * 100}% of the pod's memory.max, less the forge's own ${FORGE_OWN_MIB} MiB` | |
| 195 | 208 | : "the 4096 MiB default, the host states no memory limit"; |
| 196 | 209 | } |
| 197 | 210 |
test/memory-ceiling.test.ts +7
| @@ -19,6 +19,13 @@ describe("the memory ceiling is never a silent clamp (#3128)", () => { | ||
| 19 | 19 | expect(memoryCeiling(undefined, "/nonexistent/memory.max")).toBe(4096); |
| 20 | 20 | }); |
| 21 | 21 | |
| 22 | test("a small pod leaves the forge its own room: the check dies, not the forge (#12346)", () => { | |
| 23 | expect(memoryCeiling(undefined, pod(String(384 * 1048576)))).toBe(192); | |
| 24 | expect(memoryCeiling(undefined, pod(String(512 * 1048576)))).toBe(320); | |
| 25 | expect(memoryCeiling(undefined, pod(String(256 * 1048576)))).toBe(64); | |
| 26 | expect(memoryCeiling(undefined, pod(String(2048 * 1048576)))).toBe(1536); | |
| 27 | }); | |
| 28 | ||
| 22 | 29 | test("a declared memory above the ceiling produces a warning; within it, none", () => { |
| 23 | 30 | const saved = process.env["FORGE_CELL_MEMORY_MAX_MIB"]; |
| 24 | 31 | process.env["FORGE_CELL_MEMORY_MAX_MIB"] = "4096"; |
new above
offline