@hyperframes/engine 0.6.94 → 0.6.96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/services/chunkEncoder.d.ts +1 -1
- package/dist/services/chunkEncoder.d.ts.map +1 -1
- package/dist/services/chunkEncoder.js +50 -13
- package/dist/services/chunkEncoder.js.map +1 -1
- package/dist/services/parallelCoordinator.d.ts.map +1 -1
- package/dist/services/parallelCoordinator.js +4 -3
- package/dist/services/parallelCoordinator.js.map +1 -1
- package/dist/services/streamingEncoder.d.ts +9 -2
- package/dist/services/streamingEncoder.d.ts.map +1 -1
- package/dist/services/streamingEncoder.js +52 -15
- package/dist/services/streamingEncoder.js.map +1 -1
- package/dist/services/systemMemory.d.ts +11 -8
- package/dist/services/systemMemory.d.ts.map +1 -1
- package/dist/services/systemMemory.js +110 -9
- package/dist/services/systemMemory.js.map +1 -1
- package/package.json +2 -2
- package/src/services/chunkEncoder.test.ts +330 -1
- package/src/services/chunkEncoder.ts +70 -10
- package/src/services/parallelCoordinator.ts +4 -3
- package/src/services/streamingEncoder.test.ts +156 -10
- package/src/services/streamingEncoder.ts +70 -16
- package/src/services/systemMemory.test.ts +303 -2
- package/src/services/systemMemory.ts +137 -9
|
@@ -8,11 +8,140 @@
|
|
|
8
8
|
* it lives here once instead of being re-derived inline.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { readFileSync } from "fs";
|
|
11
12
|
import { totalmem } from "os";
|
|
12
13
|
|
|
14
|
+
const BYTES_PER_MIB = 1024 * 1024;
|
|
15
|
+
const BYTES_PER_MIB_BIGINT = BigInt(BYTES_PER_MIB);
|
|
16
|
+
// These are the paths as seen from INSIDE a container, where the runtime
|
|
17
|
+
// mounts the container's own cgroup at the namespace root — the case this
|
|
18
|
+
// probe exists for. They are deliberately not resolved via /proc/self/cgroup:
|
|
19
|
+
// on a bare host under systemd the process's real limit may live in a nested
|
|
20
|
+
// slice (e.g. /sys/fs/cgroup/user.slice/.../memory.max) that these root paths
|
|
21
|
+
// don't see, and that's acceptable — bare hosts are covered by total-RAM
|
|
22
|
+
// detection, and chasing nested slices adds fragility for no container gain.
|
|
23
|
+
const CGROUP_V2_MEMORY_MAX_PATH = "/sys/fs/cgroup/memory.max";
|
|
24
|
+
const CGROUP_V1_MEMORY_LIMIT_PATH = "/sys/fs/cgroup/memory/memory.limit_in_bytes";
|
|
25
|
+
// Kernel no-limit sentinel is page-rounded 2^63-1 (~9223372036854771712); >= 2^60 is implausible as a real limit.
|
|
26
|
+
const CGROUP_V1_NO_LIMIT_CUTOFF_BYTES = 2n ** 60n;
|
|
27
|
+
|
|
28
|
+
let _cachedCgroupLimitMb: number | null | undefined;
|
|
29
|
+
let _warnedCgroupReadFailure = false;
|
|
30
|
+
|
|
31
|
+
/** Parse cgroup v2/v1 memory limits from sysfs file contents into MiB. */
|
|
32
|
+
export function parseCgroupLimitMb(
|
|
33
|
+
v2Content: string | null,
|
|
34
|
+
v1Content: string | null,
|
|
35
|
+
): number | null {
|
|
36
|
+
if (v2Content !== null) {
|
|
37
|
+
return parseCgroupV2LimitMb(v2Content);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return parseCgroupV1LimitMb(v1Content);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function parseCgroupV2LimitMb(content: string): number | null {
|
|
44
|
+
const trimmed = content.trim();
|
|
45
|
+
if (trimmed === "max") {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return parsePositiveByteLimitMb(trimmed);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function parseCgroupV1LimitMb(content: string | null): number | null {
|
|
53
|
+
if (content === null) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return parsePositiveByteLimitMb(content.trim(), CGROUP_V1_NO_LIMIT_CUTOFF_BYTES);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function parsePositiveByteLimitMb(content: string, noLimitCutoffBytes?: bigint): number | null {
|
|
61
|
+
if (!/^\d+$/.test(content)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const bytes = BigInt(content);
|
|
66
|
+
if (bytes <= 0n) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (noLimitCutoffBytes !== undefined && bytes >= noLimitCutoffBytes) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return Number(bytes / BYTES_PER_MIB_BIGINT);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Test-only: reset the cached cgroup memory probe. */
|
|
78
|
+
export function _resetCgroupLimitCacheForTests(): void {
|
|
79
|
+
_cachedCgroupLimitMb = undefined;
|
|
80
|
+
_warnedCgroupReadFailure = false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getCgroupLimitMb(): number | null {
|
|
84
|
+
if (_cachedCgroupLimitMb !== undefined) return _cachedCgroupLimitMb;
|
|
85
|
+
|
|
86
|
+
if (process.platform !== "linux") {
|
|
87
|
+
_cachedCgroupLimitMb = null;
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const v2Content = readCgroupFile(CGROUP_V2_MEMORY_MAX_PATH);
|
|
92
|
+
const v1Content = v2Content === null ? readCgroupFile(CGROUP_V1_MEMORY_LIMIT_PATH) : null;
|
|
93
|
+
|
|
94
|
+
_cachedCgroupLimitMb = parseCgroupLimitMb(v2Content, v1Content);
|
|
95
|
+
if (_cachedCgroupLimitMb !== null) {
|
|
96
|
+
console.info(
|
|
97
|
+
`[SystemMemory] cgroup memory limit detected: ${_cachedCgroupLimitMb} MiB — ` +
|
|
98
|
+
`it governs memory-adaptive render behaviour instead of host RAM.`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return _cachedCgroupLimitMb;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function readCgroupFile(path: string): string | null {
|
|
105
|
+
try {
|
|
106
|
+
return readFileSync(path, "utf8");
|
|
107
|
+
} catch (error) {
|
|
108
|
+
const code = getErrorCode(error);
|
|
109
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
110
|
+
warnCgroupReadFailure(path, error);
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getErrorCode(error: unknown): string | undefined {
|
|
117
|
+
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return typeof error.code === "string" ? error.code : undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function formatCgroupReadError(error: unknown): string {
|
|
125
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
126
|
+
const code = getErrorCode(error);
|
|
127
|
+
return code ? `${code}: ${message}` : message;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function warnCgroupReadFailure(path: string, error: unknown): void {
|
|
131
|
+
if (_warnedCgroupReadFailure) return;
|
|
132
|
+
_warnedCgroupReadFailure = true;
|
|
133
|
+
console.warn(
|
|
134
|
+
`[SystemMemory] Unable to read cgroup memory limit at ${path} ` +
|
|
135
|
+
`(${formatCgroupReadError(error)}); falling back to host RAM.`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
13
139
|
/** Total physical RAM in MiB. */
|
|
14
140
|
export function getSystemTotalMb(): number {
|
|
15
|
-
|
|
141
|
+
const hostTotalMb = Math.floor(totalmem() / BYTES_PER_MIB);
|
|
142
|
+
const cgroupLimitMb = getCgroupLimitMb();
|
|
143
|
+
|
|
144
|
+
return cgroupLimitMb === null ? hostTotalMb : Math.min(hostTotalMb, cgroupLimitMb);
|
|
16
145
|
}
|
|
17
146
|
|
|
18
147
|
/**
|
|
@@ -38,14 +167,13 @@ export const LOW_MEMORY_TOTAL_MB_THRESHOLD = 8192;
|
|
|
38
167
|
* survive". Accepts an explicit `totalMb` so callers (and tests) can pass
|
|
39
168
|
* a known value instead of re-probing.
|
|
40
169
|
*
|
|
41
|
-
* Caveat:
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* detected correctly regardless of container nesting.
|
|
170
|
+
* Caveat: Linux cgroup v1/v2 memory limits are consulted when readable, so
|
|
171
|
+
* Docker and serverless runtimes, including Lambda tiers with readable cgroup
|
|
172
|
+
* ceilings, inherit the tighter container limit instead of the host's physical
|
|
173
|
+
* RAM. Environments that hide cgroup files should set
|
|
174
|
+
* `PRODUCER_LOW_MEMORY_MODE` explicitly rather than relying on auto-detection.
|
|
175
|
+
* Hosts whose *effective* total RAM is genuinely <= the threshold (laptops,
|
|
176
|
+
* small VMs, small Lambda tiers, small containers) are detected correctly.
|
|
49
177
|
*/
|
|
50
178
|
export function isLowMemorySystem(totalMb: number = getSystemTotalMb()): boolean {
|
|
51
179
|
return totalMb <= LOW_MEMORY_TOTAL_MB_THRESHOLD;
|