velocious 1.0.684 → 1.0.686
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/README.md +1 -1
- package/build/background-jobs/lifecycle-client.js +66 -2
- package/build/background-jobs/pooled-runner-child.js +29 -1
- package/build/background-jobs/types.js +1 -0
- package/build/background-jobs/worker.js +8 -2
- package/build/configuration-types.js +1 -1
- package/build/src/background-jobs/lifecycle-client.d.ts +12 -1
- package/build/src/background-jobs/lifecycle-client.d.ts.map +1 -1
- package/build/src/background-jobs/lifecycle-client.js +65 -3
- package/build/src/background-jobs/pooled-runner-child.js +31 -2
- package/build/src/background-jobs/types.d.ts +5 -0
- package/build/src/background-jobs/types.d.ts.map +1 -1
- package/build/src/background-jobs/types.js +2 -1
- package/build/src/background-jobs/worker.d.ts.map +1 -1
- package/build/src/background-jobs/worker.js +9 -3
- package/build/src/configuration-types.d.ts +2 -2
- package/build/src/configuration-types.js +2 -2
- package/package.json +4 -4
- package/src/background-jobs/lifecycle-client.js +66 -2
- package/src/background-jobs/pooled-runner-child.js +29 -1
- package/src/background-jobs/types.js +1 -0
- package/src/background-jobs/worker.js +8 -2
- package/src/configuration-types.js +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import { randomUUID } from "node:crypto"
|
|
4
|
+
import { readFileSync } from "node:fs"
|
|
4
5
|
import v8 from "node:v8"
|
|
5
6
|
import timeout from "awaitery/build/timeout.js"
|
|
6
7
|
import runJobPayload, { BackgroundJobPerformedFailure } from "./job-runner.js"
|
|
@@ -20,6 +21,31 @@ const MEMORY_OBSERVATION_INTERVAL_MS = 10000
|
|
|
20
21
|
/** Bound on in-flight job ids carried in a memory observation. */
|
|
21
22
|
const MEMORY_OBSERVATION_JOB_ID_LIMIT = 32
|
|
22
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Reads this process's peak resident set size (the kernel high-water mark,
|
|
26
|
+
* `VmHWM` in `/proc/self/status`). Unlike a point-in-time RSS sample, this is
|
|
27
|
+
* monotonic for the life of the process: once a burst ratchets the working set
|
|
28
|
+
* up, `VmHWM` remembers it even after garbage collection returns the pages.
|
|
29
|
+
* That makes it the honest signal for recycling a pooled child — a child that
|
|
30
|
+
* spiked to several gigabytes during a build burst keeps its ratcheted floor
|
|
31
|
+
* and must be replaced, even though its settled RSS at job-outcome time looks
|
|
32
|
+
* modest. A read failure (no `/proc`) degrades to the current RSS sample so the
|
|
33
|
+
* observation is never blocked on it.
|
|
34
|
+
* @returns {number} Peak RSS in bytes, or the current RSS sample when unreadable.
|
|
35
|
+
*/
|
|
36
|
+
function readPeakRssBytes() {
|
|
37
|
+
const fallbackBytes = process.memoryUsage().rss
|
|
38
|
+
try {
|
|
39
|
+
const status = readFileSync("/proc/self/status", "utf8")
|
|
40
|
+
const match = /^VmHWM:\s+(\d+)\s+kB$/m.exec(status)
|
|
41
|
+
if (!match) return fallbackBytes
|
|
42
|
+
const kibibytes = Number(match[1])
|
|
43
|
+
return Number.isFinite(kibibytes) && kibibytes > 0 ? kibibytes * 1024 : fallbackBytes
|
|
44
|
+
} catch {
|
|
45
|
+
return fallbackBytes
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
23
49
|
setRunnerProcessTitle()
|
|
24
50
|
|
|
25
51
|
/** @type {Promise<void> | undefined} */
|
|
@@ -162,7 +188,7 @@ function sendChildAcceptance(type, payload, observedAtMs) {
|
|
|
162
188
|
* child runs 25 concurrent jobs) distinguishes a V8 heap-growth leak from
|
|
163
189
|
* external/array-buffer (native resource) growth, and the in-flight job ids
|
|
164
190
|
* tie the observation to the work that was running.
|
|
165
|
-
* @returns {{activeJobIds: string[], activeJobIdsTruncatedCount: number, childInstanceId: string, childPid: number, childUptimeMs: number, heapStatistics: ReturnType<typeof v8.getHeapStatistics>, jobCount: number, memoryUsage: ReturnType<typeof process.memoryUsage>, observedAtMs: number, rssBytes: number, type: "pooled-child-memory", uptimeMs: number}} - Bounded memory observation for this child.
|
|
191
|
+
* @returns {{activeJobIds: string[], activeJobIdsTruncatedCount: number, childInstanceId: string, childPid: number, childUptimeMs: number, heapStatistics: ReturnType<typeof v8.getHeapStatistics>, jobCount: number, memoryUsage: ReturnType<typeof process.memoryUsage>, observedAtMs: number, peakRssBytes: number, rssBytes: number, type: "pooled-child-memory", uptimeMs: number}} - Bounded memory observation for this child.
|
|
166
192
|
*/
|
|
167
193
|
function collectMemoryObservation() {
|
|
168
194
|
const jobIds = [...runningJobIds]
|
|
@@ -179,6 +205,7 @@ function collectMemoryObservation() {
|
|
|
179
205
|
jobCount: jobIds.length,
|
|
180
206
|
memoryUsage,
|
|
181
207
|
observedAtMs: Date.now(),
|
|
208
|
+
peakRssBytes: readPeakRssBytes(),
|
|
182
209
|
rssBytes: memoryUsage.rss,
|
|
183
210
|
type: "pooled-child-memory",
|
|
184
211
|
uptimeMs: Math.floor(process.uptime() * 1000)
|
|
@@ -289,6 +316,7 @@ function sendOutcome({jobId, acknowledged, status, error}) {
|
|
|
289
316
|
jobId,
|
|
290
317
|
acknowledged,
|
|
291
318
|
status,
|
|
319
|
+
peakRssBytes: readPeakRssBytes(),
|
|
292
320
|
rssBytes: process.memoryUsage().rss,
|
|
293
321
|
error: error?.message
|
|
294
322
|
}, () => resolve(undefined))
|
|
@@ -90,6 +90,7 @@
|
|
|
90
90
|
* @property {number} jobCount - In-flight job count.
|
|
91
91
|
* @property {ReturnType<typeof import("node:process").memoryUsage>} memoryUsage - Process memory breakdown at the sample.
|
|
92
92
|
* @property {number} observedAtMs - Epoch ms the child sampled.
|
|
93
|
+
* @property {number} peakRssBytes - Child-reported peak RSS (kernel `VmHWM` high-water mark) in bytes, monotonic for the child's life.
|
|
93
94
|
* @property {number} rssBytes - Resident set size in bytes at the sample.
|
|
94
95
|
* @property {"pooled-child-memory"} type - Discriminator.
|
|
95
96
|
* @property {number} uptimeMs - Process uptime in ms.
|
|
@@ -1404,7 +1404,7 @@ export default class BackgroundJobsWorker {
|
|
|
1404
1404
|
*/
|
|
1405
1405
|
_handlePooledChildMessage({child, message}) {
|
|
1406
1406
|
if (!message || typeof message !== "object") return
|
|
1407
|
-
const record = /** @type {{type?: ReturnType<typeof JSON.parse>, childInstanceId?: ReturnType<typeof JSON.parse>, jobId?: ReturnType<typeof JSON.parse>, acknowledged?: ReturnType<typeof JSON.parse>, rssBytes?: ReturnType<typeof JSON.parse>, error?: ReturnType<typeof JSON.parse>}} */ (message)
|
|
1407
|
+
const record = /** @type {{type?: ReturnType<typeof JSON.parse>, childInstanceId?: ReturnType<typeof JSON.parse>, jobId?: ReturnType<typeof JSON.parse>, acknowledged?: ReturnType<typeof JSON.parse>, rssBytes?: ReturnType<typeof JSON.parse>, peakRssBytes?: ReturnType<typeof JSON.parse>, error?: ReturnType<typeof JSON.parse>}} */ (message)
|
|
1408
1408
|
const state = this.pooledChildStates.get(child)
|
|
1409
1409
|
if (record.type === "ready") {
|
|
1410
1410
|
if (state) {
|
|
@@ -1458,8 +1458,13 @@ export default class BackgroundJobsWorker {
|
|
|
1458
1458
|
}
|
|
1459
1459
|
|
|
1460
1460
|
const rssBytes = typeof record.rssBytes === "number" ? record.rssBytes : Number.POSITIVE_INFINITY
|
|
1461
|
+
// The child's peak RSS (VmHWM) is monotonic and outlives the settled
|
|
1462
|
+
// sample: a ratcheted burst working-set must recycle the child even when
|
|
1463
|
+
// settled RSS at outcome time looks modest. A child without peak support
|
|
1464
|
+
// falls back to the settled sample.
|
|
1465
|
+
const peakRssBytes = typeof record.peakRssBytes === "number" ? record.peakRssBytes : rssBytes
|
|
1461
1466
|
const runnerAgeMs = Date.now() - state.createdAtMs
|
|
1462
|
-
if (!state.retiring && (state.jobsRun >= this.pooledRunnerMaxJobs ||
|
|
1467
|
+
if (!state.retiring && (state.jobsRun >= this.pooledRunnerMaxJobs || peakRssBytes >= this.pooledRunnerMaxRssBytes || runnerAgeMs >= this.pooledRunnerMaxLifetimeMs || this.shouldStop)) {
|
|
1463
1468
|
this._beginRetirePooledChild(child)
|
|
1464
1469
|
}
|
|
1465
1470
|
this._terminateIfDrained(child)
|
|
@@ -1524,6 +1529,7 @@ export default class BackgroundJobsWorker {
|
|
|
1524
1529
|
childPid: message.childPid,
|
|
1525
1530
|
childUptimeS: Math.round(message.childUptimeMs / 1000),
|
|
1526
1531
|
rssMb: Math.round(message.rssBytes / (1024 * 1024)),
|
|
1532
|
+
peakRssMb: Math.round((typeof message.peakRssBytes === "number" ? message.peakRssBytes : message.rssBytes) / (1024 * 1024)),
|
|
1527
1533
|
heapUsedMb: Math.round(heap.used_heap_size / (1024 * 1024)),
|
|
1528
1534
|
heapTotalMb: Math.round(heap.total_heap_size / (1024 * 1024)),
|
|
1529
1535
|
heapLimitMb: Math.round(heap.heap_size_limit / (1024 * 1024)),
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
* @property {number} [pooledRunnerCount] - Number of warm, reusable child runners owned by each worker. Pooled capacity is separate from inline and forked/spawned capacity. Default: `4`.
|
|
195
195
|
* @property {number} [pooledRunnerConcurrency] - Number of jobs each pooled child runs concurrently on its own event loop. Total per-worker pooled capacity is `pooledRunnerCount × pooledRunnerConcurrency`. `1` (default) keeps each child serial; raise it for I/O-bound jobs so a bounded set of isolated processes handles high concurrency (like the inline lane) without one process per concurrent job. Default: `1`.
|
|
196
196
|
* @property {number} [pooledRunnerMaxJobs] - Number of sequential jobs a pooled child runs before it is replaced, bounding process-level resource accumulation. Default: `100`.
|
|
197
|
-
* @property {number} [pooledRunnerMaxRssBytes] - RSS
|
|
197
|
+
* @property {number} [pooledRunnerMaxRssBytes] - Peak RSS (the kernel `VmHWM` high-water mark reported by the child) at which a pooled child is replaced after an acknowledged job. The child's peak, not its settled RSS at job-outcome time, is the trigger, so a burst working-set that reclaims before the outcome still retires the child. Default: `536870912` (512 MiB).
|
|
198
198
|
* @property {number} [pooledRunnerMaxLifetimeMs] - Age after an acknowledged job at which a pooled child is replaced. Default: `3600000` (one hour).
|
|
199
199
|
* @property {Record<string, {maxConcurrent?: number, priority?: number}>} [queues] - Per-queue
|
|
200
200
|
* concurrency caps and dispatch priorities, Sidekiq-style. A job declares its queue (static
|