@camstack/addon-pipeline 1.2.145 → 1.2.146
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/detection-pipeline/index.js +29 -28
- package/dist/detection-pipeline/index.mjs +2 -1
- package/dist/event-loop-stall-monitor-DXLNBMkY.mjs +138 -0
- package/dist/event-loop-stall-monitor-DaUBcb13.js +149 -0
- package/dist/{event-loop-stall-monitor-DH_vFYWu.mjs → local-frame-registry-Ctv_TzmF.mjs} +1 -138
- package/dist/{event-loop-stall-monitor-DaV4Zdsz.js → local-frame-registry-DO8-LuSo.js} +0 -149
- package/dist/pipeline-runner/index.js +10 -9
- package/dist/pipeline-runner/index.mjs +2 -1
- package/dist/recorder/index.js +14 -0
- package/dist/recorder/index.mjs +14 -0
- package/package.json +1 -1
|
@@ -4,8 +4,9 @@ Object.defineProperties(exports, {
|
|
|
4
4
|
});
|
|
5
5
|
const require_dist = require("../dist-DML3mpn9.js");
|
|
6
6
|
const require_node = require("../node-OvVqe9eh.js");
|
|
7
|
-
const
|
|
7
|
+
const require_local_frame_registry = require("../local-frame-registry-DO8-LuSo.js");
|
|
8
8
|
const require_lazy_sharp = require("../lazy-sharp-BzXIjfN8.js");
|
|
9
|
+
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaUBcb13.js");
|
|
9
10
|
const require_process_memory = require("../process-memory-NWKrKfmi.js");
|
|
10
11
|
let node_fs = require("node:fs");
|
|
11
12
|
node_fs = require_dist.__toESM(node_fs);
|
|
@@ -519,7 +520,7 @@ function classifyAccelerator(backend, device) {
|
|
|
519
520
|
* missing-build fallback branch is unit-testable without a live catalog —
|
|
520
521
|
* mirrors `collectZeroBuildIssues`' injection seam.
|
|
521
522
|
*/
|
|
522
|
-
function resolveDefaultDetectionModel(backend, device, format, getStepDef =
|
|
523
|
+
function resolveDefaultDetectionModel(backend, device, format, getStepDef = require_local_frame_registry.getStepDefinition) {
|
|
523
524
|
let def;
|
|
524
525
|
try {
|
|
525
526
|
def = getStepDef(OBJECT_DETECTION_STEP_ID);
|
|
@@ -531,7 +532,7 @@ function resolveDefaultDetectionModel(backend, device, format, getStepDef = requ
|
|
|
531
532
|
const entry = def.models.find((m) => m.id === candidate);
|
|
532
533
|
if (entry && entry.formats[format] !== void 0) return candidate;
|
|
533
534
|
}
|
|
534
|
-
return
|
|
535
|
+
return require_local_frame_registry.getDefaultModelForFormatFromDef(def, format);
|
|
535
536
|
}
|
|
536
537
|
//#endregion
|
|
537
538
|
//#region src/detection-pipeline/device-override-mirror.ts
|
|
@@ -2524,7 +2525,7 @@ var EngineFactory = class {
|
|
|
2524
2525
|
* to `buildConfig`, which throws the message naming the step and the model.
|
|
2525
2526
|
*/
|
|
2526
2527
|
resolveDecode(stepId, modelId, settings) {
|
|
2527
|
-
const def =
|
|
2528
|
+
const def = require_local_frame_registry.getStepDefinition(stepId);
|
|
2528
2529
|
const modelEntry = def.models.find((m) => m.id === modelId) ?? this.opts.resolveCustomModel?.(stepId, modelId);
|
|
2529
2530
|
if (!modelEntry) return null;
|
|
2530
2531
|
return resolvePoolDecodeSettings({
|
|
@@ -2568,7 +2569,7 @@ var EngineFactory = class {
|
|
|
2568
2569
|
*/
|
|
2569
2570
|
function buildPoolModelConfigForStep(inputs) {
|
|
2570
2571
|
const { stepId, modelId, poolRuntime, modelsDir } = inputs;
|
|
2571
|
-
const def =
|
|
2572
|
+
const def = require_local_frame_registry.getStepDefinition(stepId);
|
|
2572
2573
|
const modelEntry = def.models.find((m) => m.id === modelId) ?? inputs.resolveCustomModel?.(stepId, modelId);
|
|
2573
2574
|
if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${stepId}" catalog (static or custom registry)`);
|
|
2574
2575
|
const format = RUNTIME_TO_FORMAT[poolRuntime];
|
|
@@ -5427,7 +5428,7 @@ var PipelineExecutor = class {
|
|
|
5427
5428
|
this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0, deviceId);
|
|
5428
5429
|
continue;
|
|
5429
5430
|
}
|
|
5430
|
-
const precision =
|
|
5431
|
+
const precision = require_local_frame_registry.landmarkPrecisionVerdict({
|
|
5431
5432
|
detectionShortSidePx: detShortSide,
|
|
5432
5433
|
floorPx: numericSetting(child.settings, "minLandmarkFaceSize", 24)
|
|
5433
5434
|
});
|
|
@@ -5690,7 +5691,7 @@ function buildExecutableTree(steps, getEngine, resolveModel) {
|
|
|
5690
5691
|
return { roots: steps.filter((s) => s.enabled).filter((s) => s.slot !== "audio-classifier").map((s) => buildNode(s, getEngine, resolveModel)) };
|
|
5691
5692
|
}
|
|
5692
5693
|
function buildNode(step, getEngine, resolveModel) {
|
|
5693
|
-
const definition =
|
|
5694
|
+
const definition = require_local_frame_registry.getStepDefinition(step.addonId);
|
|
5694
5695
|
const engine = getEngine(step.addonId);
|
|
5695
5696
|
const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildNode(c, getEngine, resolveModel));
|
|
5696
5697
|
const mergedSettings = {
|
|
@@ -5717,7 +5718,7 @@ function buildNode(step, getEngine, resolveModel) {
|
|
|
5717
5718
|
* entered recursively. Fields without a declared default are skipped.
|
|
5718
5719
|
*/
|
|
5719
5720
|
function collectSchemaDefaults(stepId) {
|
|
5720
|
-
const schema =
|
|
5721
|
+
const schema = require_local_frame_registry.getStep(stepId).getConfigSchema();
|
|
5721
5722
|
const out = {};
|
|
5722
5723
|
walkFieldsForDefaults(schema, out);
|
|
5723
5724
|
return out;
|
|
@@ -5811,7 +5812,7 @@ function mergeCustomModels(staticModels, customModels) {
|
|
|
5811
5812
|
*/
|
|
5812
5813
|
var defaultLookup = (stepId) => {
|
|
5813
5814
|
try {
|
|
5814
|
-
return
|
|
5815
|
+
return require_local_frame_registry.getStepDefinition(stepId);
|
|
5815
5816
|
} catch {
|
|
5816
5817
|
return null;
|
|
5817
5818
|
}
|
|
@@ -5860,7 +5861,7 @@ function collectSubstitutions(steps, format) {
|
|
|
5860
5861
|
for (const step of steps) {
|
|
5861
5862
|
const chosen = step.modelId;
|
|
5862
5863
|
if (chosen != null) {
|
|
5863
|
-
const running =
|
|
5864
|
+
const running = require_local_frame_registry.resolveModelForFormat(step.addonId, chosen, format);
|
|
5864
5865
|
if (running !== chosen) result.push({
|
|
5865
5866
|
addonId: step.addonId,
|
|
5866
5867
|
chosen,
|
|
@@ -5886,7 +5887,7 @@ function collectSubstitutions(steps, format) {
|
|
|
5886
5887
|
* black-box test. Unknown/custom addonIds (no registered `StepDefinition`)
|
|
5887
5888
|
* are skipped, never thrown.
|
|
5888
5889
|
*/
|
|
5889
|
-
function collectZeroBuildIssues(steps, format, getStepDef =
|
|
5890
|
+
function collectZeroBuildIssues(steps, format, getStepDef = require_local_frame_registry.getStepDefinition) {
|
|
5890
5891
|
const result = [];
|
|
5891
5892
|
for (const step of steps) {
|
|
5892
5893
|
let def;
|
|
@@ -5912,7 +5913,7 @@ function collectZeroBuildIssues(steps, format, getStepDef = require_event_loop_s
|
|
|
5912
5913
|
* tests can exercise the unknown-addon branch with a fabricated resolver
|
|
5913
5914
|
* without `vi.mock`.
|
|
5914
5915
|
*/
|
|
5915
|
-
function collectUnknownAddonIssues(steps, getStepDef =
|
|
5916
|
+
function collectUnknownAddonIssues(steps, getStepDef = require_local_frame_registry.getStepDefinition) {
|
|
5916
5917
|
const result = [];
|
|
5917
5918
|
for (const step of steps) {
|
|
5918
5919
|
try {
|
|
@@ -5953,7 +5954,7 @@ function resolveInputSteps(steps, format, engine, clusterModels, clusterSettings
|
|
|
5953
5954
|
for (const s of steps) {
|
|
5954
5955
|
let def;
|
|
5955
5956
|
try {
|
|
5956
|
-
def =
|
|
5957
|
+
def = require_local_frame_registry.getStepDefinition(s.addonId);
|
|
5957
5958
|
} catch {
|
|
5958
5959
|
unknownAddonIds.push(s.addonId);
|
|
5959
5960
|
continue;
|
|
@@ -5989,7 +5990,7 @@ function resolveInputSteps(steps, format, engine, clusterModels, clusterSettings
|
|
|
5989
5990
|
}
|
|
5990
5991
|
}
|
|
5991
5992
|
const chosenModelId = s.modelId ?? null;
|
|
5992
|
-
const runningModelId = chosenModelId === null && engine !== void 0 && s.addonId === "object-detection" ? resolveDefaultDetectionModel(engine.backend, engine.device, format) :
|
|
5993
|
+
const runningModelId = chosenModelId === null && engine !== void 0 && s.addonId === "object-detection" ? resolveDefaultDetectionModel(engine.backend, engine.device, format) : require_local_frame_registry.resolveModelForFormat(s.addonId, chosenModelId, format);
|
|
5993
5994
|
if (chosenModelId !== null && runningModelId !== chosenModelId) substitutions.push({
|
|
5994
5995
|
addonId: s.addonId,
|
|
5995
5996
|
chosen: chosenModelId,
|
|
@@ -7004,7 +7005,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
7004
7005
|
* first, then the custom registry. Returns undefined if neither has it.
|
|
7005
7006
|
*/
|
|
7006
7007
|
async resolveModelEntry(addonId, modelId) {
|
|
7007
|
-
const fromCatalog =
|
|
7008
|
+
const fromCatalog = require_local_frame_registry.getStepDefinition(addonId).models.find((m) => m.id === modelId);
|
|
7008
7009
|
if (fromCatalog) return fromCatalog;
|
|
7009
7010
|
return (await this.getCustomModels()).get(addonId)?.find((m) => m.id === modelId);
|
|
7010
7011
|
}
|
|
@@ -7100,7 +7101,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
7100
7101
|
/** Pure recursive resolver — builds the resolved step tree without touching `modelSubstitutions`. */
|
|
7101
7102
|
resolveStepModelTree(steps, format) {
|
|
7102
7103
|
return steps.map((step) => {
|
|
7103
|
-
const resolved =
|
|
7104
|
+
const resolved = require_local_frame_registry.resolveModelForFormat(step.addonId, step.modelId ?? null, format);
|
|
7104
7105
|
const children = step.children?.length ? this.resolveStepModelTree([...step.children], format) : step.children;
|
|
7105
7106
|
return resolved === step.modelId && children === step.children ? step : {
|
|
7106
7107
|
...step,
|
|
@@ -7248,7 +7249,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
7248
7249
|
for (const step of nodes) {
|
|
7249
7250
|
if (!step.enabled) continue;
|
|
7250
7251
|
try {
|
|
7251
|
-
if (
|
|
7252
|
+
if (require_local_frame_registry.getStepDefinition(step.addonId).slot === "audio-classifier") continue;
|
|
7252
7253
|
} catch {
|
|
7253
7254
|
continue;
|
|
7254
7255
|
}
|
|
@@ -7268,7 +7269,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
7268
7269
|
async getCapabilities(_forceRefresh) {
|
|
7269
7270
|
const agentId = (this.addonCtx?.kernel?.cluster?.broker)?.nodeID ?? node_os.hostname();
|
|
7270
7271
|
const agentName = agentId;
|
|
7271
|
-
const addons =
|
|
7272
|
+
const addons = require_local_frame_registry.ALL_PIPELINE_STEPS.map((s) => ({
|
|
7272
7273
|
id: s.definition.id,
|
|
7273
7274
|
name: s.definition.name,
|
|
7274
7275
|
models: s.definition.models.filter((m) => m.legacy !== true).map((m) => {
|
|
@@ -7766,7 +7767,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
7766
7767
|
let frameViewResolver;
|
|
7767
7768
|
let rootInputViewProvider;
|
|
7768
7769
|
if (runtimeFrameRef) {
|
|
7769
|
-
frameViewResolver = createRootFrameViewResolver(
|
|
7770
|
+
frameViewResolver = createRootFrameViewResolver(require_local_frame_registry.localFrameRegistry, runtimeFrameRef);
|
|
7770
7771
|
rootInputViewProvider = async (root, crop) => {
|
|
7771
7772
|
const view = await frameViewResolver(root, crop ? {
|
|
7772
7773
|
left: crop[0],
|
|
@@ -9213,7 +9214,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
|
|
|
9213
9214
|
function buildSchemaSlots(formats, modelsDir, customByStep) {
|
|
9214
9215
|
const slotMap = /* @__PURE__ */ new Map();
|
|
9215
9216
|
const formatList = [...formats];
|
|
9216
|
-
for (const pipelineStep of
|
|
9217
|
+
for (const pipelineStep of require_local_frame_registry.ALL_PIPELINE_STEPS) {
|
|
9217
9218
|
const step = pipelineStep.definition;
|
|
9218
9219
|
const availableModels = mergeCustomModels(step.models, customByStep?.get(step.id) ?? []).filter((m) => m.legacy !== true && formatList.some((f) => m.formats[f]));
|
|
9219
9220
|
if (availableModels.length === 0) continue;
|
|
@@ -9307,7 +9308,7 @@ function resolveChildSlots(slot) {
|
|
|
9307
9308
|
*/
|
|
9308
9309
|
function buildDefaultStepTree(format, objectDetectionModelId) {
|
|
9309
9310
|
const makeStep = (stepId, children, opts) => {
|
|
9310
|
-
const def =
|
|
9311
|
+
const def = require_local_frame_registry.getStepDefinition(stepId);
|
|
9311
9312
|
if (!def.models.some((m) => m.formats[format])) return null;
|
|
9312
9313
|
return {
|
|
9313
9314
|
addonId: def.id,
|
|
@@ -9316,7 +9317,7 @@ function buildDefaultStepTree(format, objectDetectionModelId) {
|
|
|
9316
9317
|
inputClasses: def.inputClasses ?? [],
|
|
9317
9318
|
outputClasses: def.outputClasses,
|
|
9318
9319
|
enabled: opts?.enabled ?? true,
|
|
9319
|
-
modelId: opts?.modelId ??
|
|
9320
|
+
modelId: opts?.modelId ?? require_local_frame_registry.getDefaultModelForFormat(stepId, format),
|
|
9320
9321
|
children,
|
|
9321
9322
|
...def.group ? { group: def.group } : {}
|
|
9322
9323
|
};
|
|
@@ -9373,7 +9374,7 @@ function flattenSteps(steps) {
|
|
|
9373
9374
|
*/
|
|
9374
9375
|
function isDetailPlaneStep(addonId) {
|
|
9375
9376
|
try {
|
|
9376
|
-
return
|
|
9377
|
+
return require_local_frame_registry.getStepDefinition(addonId).inputClasses !== null;
|
|
9377
9378
|
} catch {
|
|
9378
9379
|
return false;
|
|
9379
9380
|
}
|
|
@@ -9394,7 +9395,7 @@ function stepHasZeroBuildsFor(addonId, format) {
|
|
|
9394
9395
|
*/
|
|
9395
9396
|
function availableFormatsForStep(addonId) {
|
|
9396
9397
|
try {
|
|
9397
|
-
const def =
|
|
9398
|
+
const def = require_local_frame_registry.getStepDefinition(addonId);
|
|
9398
9399
|
const formats = /* @__PURE__ */ new Set();
|
|
9399
9400
|
for (const model of def.models) for (const format of Object.keys(model.formats)) formats.add(format);
|
|
9400
9401
|
return Array.from(formats).toSorted();
|
|
@@ -9963,7 +9964,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
9963
9964
|
}).catch(() => {});
|
|
9964
9965
|
}
|
|
9965
9966
|
getModelCatalog() {
|
|
9966
|
-
return
|
|
9967
|
+
return require_local_frame_registry.ALL_STEPS.flatMap((s) => s.models.filter((m) => m.legacy !== true));
|
|
9967
9968
|
}
|
|
9968
9969
|
/**
|
|
9969
9970
|
* Proactively install the OpenVINO Python package when Intel hardware
|
|
@@ -10049,9 +10050,9 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
|
|
|
10049
10050
|
}
|
|
10050
10051
|
};
|
|
10051
10052
|
//#endregion
|
|
10052
|
-
exports.ALL_STEPS =
|
|
10053
|
+
exports.ALL_STEPS = require_local_frame_registry.ALL_STEPS;
|
|
10053
10054
|
exports.DetectionPipelineProvider = DetectionPipelineProvider;
|
|
10054
10055
|
exports.default = DetectionPipelineAddon;
|
|
10055
|
-
exports.getDefaultModelForFormat =
|
|
10056
|
-
exports.getStepDefinition =
|
|
10056
|
+
exports.getDefaultModelForFormat = require_local_frame_registry.getDefaultModelForFormat;
|
|
10057
|
+
exports.getStepDefinition = require_local_frame_registry.getStepDefinition;
|
|
10057
10058
|
exports.shouldInstallOpenvino = shouldInstallOpenvino;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { n as __require } from "../chunk-DnnnRqeS.mjs";
|
|
2
2
|
import { Bt as nodePin, Ft as createEvent, It as hydrateSchema, Kt as array, O as YAMNET_TO_MACRO, Q as inferModelProvider, Qt as object, S as PoolMemoryWatchdog, St as supportedRuntimes$1, Vt as parseJsonUnknown, W as detectionPipelineCapability, Wt as sleep, Y as evaluateZoneRules, _t as resolvePoolMemoryPolicy, at as overlayClusterStepSettings, c as DEFAULT_CLUSTER_STEP_MODELS, ct as pickClusterStepSettings, dt as pipelineExecutorCapability, en as string, et as loadContributionCapability, ht as resolveClusterStepModelId, jt as BaseAddon, l as DEFAULT_CLUSTER_STEP_SETTINGS, m as DEVICE_BACKEND_TO_FORMAT, nn as EventCategory, q as enumerateInferenceDevices, st as pickClusterStepModels, t as APPLE_SA_TO_MACRO, tn as union, wt as errMsg, yt as runtimeDevices$1, z as defaultDeviceFor$1 } from "../dist-cZopp8Xm.mjs";
|
|
3
3
|
import { a as readProcessCost } from "../node-Br6p9KvD.mjs";
|
|
4
|
-
import { a as
|
|
4
|
+
import { a as getDefaultModelForFormat, c as getStepDefinition, i as ALL_STEPS, l as resolveModelForFormat, o as getDefaultModelForFormatFromDef, r as ALL_PIPELINE_STEPS, s as getStep, t as localFrameRegistry, u as landmarkPrecisionVerdict } from "../local-frame-registry-Ctv_TzmF.mjs";
|
|
5
5
|
import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
|
|
6
|
+
import { n as startEventLoopStallMonitor } from "../event-loop-stall-monitor-DXLNBMkY.mjs";
|
|
6
7
|
import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-DX25Z60Q.mjs";
|
|
7
8
|
import * as fs from "node:fs";
|
|
8
9
|
import * as path$1 from "node:path";
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { PerformanceObserver } from "node:perf_hooks";
|
|
2
|
+
//#region src/detection-pipeline/diagnostics/event-loop-stall-monitor.ts
|
|
3
|
+
/**
|
|
4
|
+
* event-loop-stall-monitor — names the thing that freezes the detection process.
|
|
5
|
+
*
|
|
6
|
+
* ## What this exists to settle
|
|
7
|
+
*
|
|
8
|
+
* On 2026-08-01/02 inference across the whole fleet stalled in bursts: 759
|
|
9
|
+
* calls in 90 minutes crossed the runner's 20s deadline, p50 41s and max 80s,
|
|
10
|
+
* and EVERY one of them eventually resolved. Six explanations were killed with
|
|
11
|
+
* telemetry alone:
|
|
12
|
+
*
|
|
13
|
+
* - not a slow GPU or a wedged pool — the work completes, just enormously late;
|
|
14
|
+
* - not CPU starvation — hosts sat at 8-48% during the freezes;
|
|
15
|
+
* - not swap — `pswpin/pswpout` are flat zero on every host;
|
|
16
|
+
* - not a logging artifact — Loki's timestamp trails the in-process one by
|
|
17
|
+
* 0.1-1.0s at p95, so the gaps are real, not buffered delivery;
|
|
18
|
+
* - not the v1.2.30 release — the stalls predate it by a day;
|
|
19
|
+
* - not only the idle-pool recompile — that is real and hub-side, but the
|
|
20
|
+
* agent stalls with no eviction and no compile at all.
|
|
21
|
+
*
|
|
22
|
+
* What IS established: the 10s stats timer — which touches no inference code —
|
|
23
|
+
* stops firing for 22-88s and resumes at the same instant the abandoned calls
|
|
24
|
+
* settle. The event loop itself freezes. From outside the process, GC and a
|
|
25
|
+
* synchronous block are indistinguishable, and guessing between them is how
|
|
26
|
+
* this investigation would go wrong. So measure it.
|
|
27
|
+
*
|
|
28
|
+
* ## How
|
|
29
|
+
*
|
|
30
|
+
* A 1s interval whose LATENESS is the freeze: a tick that arrives 40s late means
|
|
31
|
+
* the loop was blocked ~39s, whatever blocked it. In the same window the GC
|
|
32
|
+
* observer accumulates pause time. The two numbers together answer the question
|
|
33
|
+
* outright:
|
|
34
|
+
*
|
|
35
|
+
* - `gcMs` ≈ `stallMs` → garbage collection. Look at heap growth.
|
|
36
|
+
* - `gcMs` ≈ 0 → something SYNCHRONOUS blocked the loop, and GC is innocent.
|
|
37
|
+
*
|
|
38
|
+
* Heap and RSS ride along because if it is GC, the next question is always
|
|
39
|
+
* "growing since when?".
|
|
40
|
+
*
|
|
41
|
+
* Deliberately not a histogram (`monitorEventLoopDelay`): that reports a
|
|
42
|
+
* distribution, and the question here is about ONE outlier and what coincided
|
|
43
|
+
* with it.
|
|
44
|
+
*/
|
|
45
|
+
/** Tick period. Small enough to bound attribution error, large enough to cost
|
|
46
|
+
* nothing on a process already doing frame work. */
|
|
47
|
+
var STALL_TICK_MS = 1e3;
|
|
48
|
+
/**
|
|
49
|
+
* Report a freeze only past this. The runner's own inference deadline is 20s
|
|
50
|
+
* and the observed freezes run 22-88s, so this is far below the signal while
|
|
51
|
+
* staying clear of ordinary scheduling jitter (a busy frame path routinely
|
|
52
|
+
* overruns a 1s timer by tens of ms).
|
|
53
|
+
*/
|
|
54
|
+
var STALL_REPORT_MS = 2e3;
|
|
55
|
+
/**
|
|
56
|
+
* Attribute a freeze. Pure, because this is the ONE judgement the whole module
|
|
57
|
+
* exists to make and it must be testable without freezing a real event loop.
|
|
58
|
+
*
|
|
59
|
+
* `gc` when garbage collection accounts for most of the stall; a
|
|
60
|
+
* `synchronous-block` when it accounts for almost none — that verdict points
|
|
61
|
+
* away from the heap entirely and at whatever ran without yielding.
|
|
62
|
+
*/
|
|
63
|
+
function attributeStall(stallMs, gcMs, gcCount, longestGcMs) {
|
|
64
|
+
const share = stallMs > 0 ? gcMs / stallMs : 0;
|
|
65
|
+
return {
|
|
66
|
+
stallMs,
|
|
67
|
+
gcMs,
|
|
68
|
+
gcCount,
|
|
69
|
+
longestGcMs,
|
|
70
|
+
cause: share >= .7 ? "gc" : share <= .2 ? "synchronous-block" : "mixed"
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Start watching. Returns a stop function; safe to call twice.
|
|
75
|
+
*
|
|
76
|
+
* Never throws into the caller: a diagnostic that can take the detection
|
|
77
|
+
* process down is worse than no diagnostic. `PerformanceObserver` for `gc` is
|
|
78
|
+
* unavailable in some embedded runtimes, and that must degrade to
|
|
79
|
+
* stall-detection-without-attribution rather than a boot failure.
|
|
80
|
+
*/
|
|
81
|
+
function startEventLoopStallMonitor(logger, reportThresholdMs = STALL_REPORT_MS) {
|
|
82
|
+
let gc = {
|
|
83
|
+
totalMs: 0,
|
|
84
|
+
count: 0,
|
|
85
|
+
longestMs: 0
|
|
86
|
+
};
|
|
87
|
+
let observer = null;
|
|
88
|
+
try {
|
|
89
|
+
observer = new PerformanceObserver((list) => {
|
|
90
|
+
for (const entry of list.getEntries()) {
|
|
91
|
+
gc.totalMs += entry.duration;
|
|
92
|
+
gc.count += 1;
|
|
93
|
+
if (entry.duration > gc.longestMs) gc.longestMs = entry.duration;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
observer.observe({ entryTypes: ["gc"] });
|
|
97
|
+
} catch {
|
|
98
|
+
observer = null;
|
|
99
|
+
}
|
|
100
|
+
let expected = Date.now() + STALL_TICK_MS;
|
|
101
|
+
const timer = setInterval(() => {
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
const lateBy = now - expected;
|
|
104
|
+
const window = gc;
|
|
105
|
+
gc = {
|
|
106
|
+
totalMs: 0,
|
|
107
|
+
count: 0,
|
|
108
|
+
longestMs: 0
|
|
109
|
+
};
|
|
110
|
+
expected = now + STALL_TICK_MS;
|
|
111
|
+
if (lateBy < reportThresholdMs) return;
|
|
112
|
+
const verdict = attributeStall(lateBy, Math.round(window.totalMs), window.count, Math.round(window.longestMs));
|
|
113
|
+
const mem = process.memoryUsage();
|
|
114
|
+
logger.warn("event loop stalled", { meta: {
|
|
115
|
+
stallMs: verdict.stallMs,
|
|
116
|
+
cause: verdict.cause,
|
|
117
|
+
gcMs: verdict.gcMs,
|
|
118
|
+
gcCount: verdict.gcCount,
|
|
119
|
+
longestGcMs: verdict.longestGcMs,
|
|
120
|
+
gcObserved: observer !== null,
|
|
121
|
+
heapUsedMb: Math.round(mem.heapUsed / 1e6),
|
|
122
|
+
heapTotalMb: Math.round(mem.heapTotal / 1e6),
|
|
123
|
+
rssMb: Math.round(mem.rss / 1e6),
|
|
124
|
+
externalMb: Math.round(mem.external / 1e6),
|
|
125
|
+
arrayBuffersMb: Math.round(mem.arrayBuffers / 1e6)
|
|
126
|
+
} });
|
|
127
|
+
}, STALL_TICK_MS);
|
|
128
|
+
timer.unref?.();
|
|
129
|
+
let stopped = false;
|
|
130
|
+
return () => {
|
|
131
|
+
if (stopped) return;
|
|
132
|
+
stopped = true;
|
|
133
|
+
clearInterval(timer);
|
|
134
|
+
observer?.disconnect();
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
export { startEventLoopStallMonitor as n, attributeStall as t };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
let node_perf_hooks = require("node:perf_hooks");
|
|
2
|
+
//#region src/detection-pipeline/diagnostics/event-loop-stall-monitor.ts
|
|
3
|
+
/**
|
|
4
|
+
* event-loop-stall-monitor — names the thing that freezes the detection process.
|
|
5
|
+
*
|
|
6
|
+
* ## What this exists to settle
|
|
7
|
+
*
|
|
8
|
+
* On 2026-08-01/02 inference across the whole fleet stalled in bursts: 759
|
|
9
|
+
* calls in 90 minutes crossed the runner's 20s deadline, p50 41s and max 80s,
|
|
10
|
+
* and EVERY one of them eventually resolved. Six explanations were killed with
|
|
11
|
+
* telemetry alone:
|
|
12
|
+
*
|
|
13
|
+
* - not a slow GPU or a wedged pool — the work completes, just enormously late;
|
|
14
|
+
* - not CPU starvation — hosts sat at 8-48% during the freezes;
|
|
15
|
+
* - not swap — `pswpin/pswpout` are flat zero on every host;
|
|
16
|
+
* - not a logging artifact — Loki's timestamp trails the in-process one by
|
|
17
|
+
* 0.1-1.0s at p95, so the gaps are real, not buffered delivery;
|
|
18
|
+
* - not the v1.2.30 release — the stalls predate it by a day;
|
|
19
|
+
* - not only the idle-pool recompile — that is real and hub-side, but the
|
|
20
|
+
* agent stalls with no eviction and no compile at all.
|
|
21
|
+
*
|
|
22
|
+
* What IS established: the 10s stats timer — which touches no inference code —
|
|
23
|
+
* stops firing for 22-88s and resumes at the same instant the abandoned calls
|
|
24
|
+
* settle. The event loop itself freezes. From outside the process, GC and a
|
|
25
|
+
* synchronous block are indistinguishable, and guessing between them is how
|
|
26
|
+
* this investigation would go wrong. So measure it.
|
|
27
|
+
*
|
|
28
|
+
* ## How
|
|
29
|
+
*
|
|
30
|
+
* A 1s interval whose LATENESS is the freeze: a tick that arrives 40s late means
|
|
31
|
+
* the loop was blocked ~39s, whatever blocked it. In the same window the GC
|
|
32
|
+
* observer accumulates pause time. The two numbers together answer the question
|
|
33
|
+
* outright:
|
|
34
|
+
*
|
|
35
|
+
* - `gcMs` ≈ `stallMs` → garbage collection. Look at heap growth.
|
|
36
|
+
* - `gcMs` ≈ 0 → something SYNCHRONOUS blocked the loop, and GC is innocent.
|
|
37
|
+
*
|
|
38
|
+
* Heap and RSS ride along because if it is GC, the next question is always
|
|
39
|
+
* "growing since when?".
|
|
40
|
+
*
|
|
41
|
+
* Deliberately not a histogram (`monitorEventLoopDelay`): that reports a
|
|
42
|
+
* distribution, and the question here is about ONE outlier and what coincided
|
|
43
|
+
* with it.
|
|
44
|
+
*/
|
|
45
|
+
/** Tick period. Small enough to bound attribution error, large enough to cost
|
|
46
|
+
* nothing on a process already doing frame work. */
|
|
47
|
+
var STALL_TICK_MS = 1e3;
|
|
48
|
+
/**
|
|
49
|
+
* Report a freeze only past this. The runner's own inference deadline is 20s
|
|
50
|
+
* and the observed freezes run 22-88s, so this is far below the signal while
|
|
51
|
+
* staying clear of ordinary scheduling jitter (a busy frame path routinely
|
|
52
|
+
* overruns a 1s timer by tens of ms).
|
|
53
|
+
*/
|
|
54
|
+
var STALL_REPORT_MS = 2e3;
|
|
55
|
+
/**
|
|
56
|
+
* Attribute a freeze. Pure, because this is the ONE judgement the whole module
|
|
57
|
+
* exists to make and it must be testable without freezing a real event loop.
|
|
58
|
+
*
|
|
59
|
+
* `gc` when garbage collection accounts for most of the stall; a
|
|
60
|
+
* `synchronous-block` when it accounts for almost none — that verdict points
|
|
61
|
+
* away from the heap entirely and at whatever ran without yielding.
|
|
62
|
+
*/
|
|
63
|
+
function attributeStall(stallMs, gcMs, gcCount, longestGcMs) {
|
|
64
|
+
const share = stallMs > 0 ? gcMs / stallMs : 0;
|
|
65
|
+
return {
|
|
66
|
+
stallMs,
|
|
67
|
+
gcMs,
|
|
68
|
+
gcCount,
|
|
69
|
+
longestGcMs,
|
|
70
|
+
cause: share >= .7 ? "gc" : share <= .2 ? "synchronous-block" : "mixed"
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Start watching. Returns a stop function; safe to call twice.
|
|
75
|
+
*
|
|
76
|
+
* Never throws into the caller: a diagnostic that can take the detection
|
|
77
|
+
* process down is worse than no diagnostic. `PerformanceObserver` for `gc` is
|
|
78
|
+
* unavailable in some embedded runtimes, and that must degrade to
|
|
79
|
+
* stall-detection-without-attribution rather than a boot failure.
|
|
80
|
+
*/
|
|
81
|
+
function startEventLoopStallMonitor(logger, reportThresholdMs = STALL_REPORT_MS) {
|
|
82
|
+
let gc = {
|
|
83
|
+
totalMs: 0,
|
|
84
|
+
count: 0,
|
|
85
|
+
longestMs: 0
|
|
86
|
+
};
|
|
87
|
+
let observer = null;
|
|
88
|
+
try {
|
|
89
|
+
observer = new node_perf_hooks.PerformanceObserver((list) => {
|
|
90
|
+
for (const entry of list.getEntries()) {
|
|
91
|
+
gc.totalMs += entry.duration;
|
|
92
|
+
gc.count += 1;
|
|
93
|
+
if (entry.duration > gc.longestMs) gc.longestMs = entry.duration;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
observer.observe({ entryTypes: ["gc"] });
|
|
97
|
+
} catch {
|
|
98
|
+
observer = null;
|
|
99
|
+
}
|
|
100
|
+
let expected = Date.now() + STALL_TICK_MS;
|
|
101
|
+
const timer = setInterval(() => {
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
const lateBy = now - expected;
|
|
104
|
+
const window = gc;
|
|
105
|
+
gc = {
|
|
106
|
+
totalMs: 0,
|
|
107
|
+
count: 0,
|
|
108
|
+
longestMs: 0
|
|
109
|
+
};
|
|
110
|
+
expected = now + STALL_TICK_MS;
|
|
111
|
+
if (lateBy < reportThresholdMs) return;
|
|
112
|
+
const verdict = attributeStall(lateBy, Math.round(window.totalMs), window.count, Math.round(window.longestMs));
|
|
113
|
+
const mem = process.memoryUsage();
|
|
114
|
+
logger.warn("event loop stalled", { meta: {
|
|
115
|
+
stallMs: verdict.stallMs,
|
|
116
|
+
cause: verdict.cause,
|
|
117
|
+
gcMs: verdict.gcMs,
|
|
118
|
+
gcCount: verdict.gcCount,
|
|
119
|
+
longestGcMs: verdict.longestGcMs,
|
|
120
|
+
gcObserved: observer !== null,
|
|
121
|
+
heapUsedMb: Math.round(mem.heapUsed / 1e6),
|
|
122
|
+
heapTotalMb: Math.round(mem.heapTotal / 1e6),
|
|
123
|
+
rssMb: Math.round(mem.rss / 1e6),
|
|
124
|
+
externalMb: Math.round(mem.external / 1e6),
|
|
125
|
+
arrayBuffersMb: Math.round(mem.arrayBuffers / 1e6)
|
|
126
|
+
} });
|
|
127
|
+
}, STALL_TICK_MS);
|
|
128
|
+
timer.unref?.();
|
|
129
|
+
let stopped = false;
|
|
130
|
+
return () => {
|
|
131
|
+
if (stopped) return;
|
|
132
|
+
stopped = true;
|
|
133
|
+
clearInterval(timer);
|
|
134
|
+
observer?.disconnect();
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
Object.defineProperty(exports, "attributeStall", {
|
|
139
|
+
enumerable: true,
|
|
140
|
+
get: function() {
|
|
141
|
+
return attributeStall;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
Object.defineProperty(exports, "startEventLoopStallMonitor", {
|
|
145
|
+
enumerable: true,
|
|
146
|
+
get: function() {
|
|
147
|
+
return startEventLoopStallMonitor;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { M as audioKindId, Z as hfModelUrl, a as COCO_80_LABELS, o as COCO_TO_MACRO, r as AUDIO_MACRO_LABELS } from "./dist-cZopp8Xm.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { PerformanceObserver } from "node:perf_hooks";
|
|
4
3
|
//#region src/detection-pipeline/registry/model-catalogs.ts
|
|
5
4
|
var HF_REPO = "camstack/camstack-models";
|
|
6
5
|
var HF_SCRYPTED = "scrypted/plugin-models";
|
|
@@ -2111,140 +2110,4 @@ var localFrameRegistry = new LocalFrameRegistry({
|
|
|
2111
2110
|
maxBytes: envPositiveInt("CAMSTACK_LOCAL_FRAME_BUDGET_MB", DEFAULT_MAX_BYTES / 1048576) * 1048576
|
|
2112
2111
|
});
|
|
2113
2112
|
//#endregion
|
|
2114
|
-
|
|
2115
|
-
/**
|
|
2116
|
-
* event-loop-stall-monitor — names the thing that freezes the detection process.
|
|
2117
|
-
*
|
|
2118
|
-
* ## What this exists to settle
|
|
2119
|
-
*
|
|
2120
|
-
* On 2026-08-01/02 inference across the whole fleet stalled in bursts: 759
|
|
2121
|
-
* calls in 90 minutes crossed the runner's 20s deadline, p50 41s and max 80s,
|
|
2122
|
-
* and EVERY one of them eventually resolved. Six explanations were killed with
|
|
2123
|
-
* telemetry alone:
|
|
2124
|
-
*
|
|
2125
|
-
* - not a slow GPU or a wedged pool — the work completes, just enormously late;
|
|
2126
|
-
* - not CPU starvation — hosts sat at 8-48% during the freezes;
|
|
2127
|
-
* - not swap — `pswpin/pswpout` are flat zero on every host;
|
|
2128
|
-
* - not a logging artifact — Loki's timestamp trails the in-process one by
|
|
2129
|
-
* 0.1-1.0s at p95, so the gaps are real, not buffered delivery;
|
|
2130
|
-
* - not the v1.2.30 release — the stalls predate it by a day;
|
|
2131
|
-
* - not only the idle-pool recompile — that is real and hub-side, but the
|
|
2132
|
-
* agent stalls with no eviction and no compile at all.
|
|
2133
|
-
*
|
|
2134
|
-
* What IS established: the 10s stats timer — which touches no inference code —
|
|
2135
|
-
* stops firing for 22-88s and resumes at the same instant the abandoned calls
|
|
2136
|
-
* settle. The event loop itself freezes. From outside the process, GC and a
|
|
2137
|
-
* synchronous block are indistinguishable, and guessing between them is how
|
|
2138
|
-
* this investigation would go wrong. So measure it.
|
|
2139
|
-
*
|
|
2140
|
-
* ## How
|
|
2141
|
-
*
|
|
2142
|
-
* A 1s interval whose LATENESS is the freeze: a tick that arrives 40s late means
|
|
2143
|
-
* the loop was blocked ~39s, whatever blocked it. In the same window the GC
|
|
2144
|
-
* observer accumulates pause time. The two numbers together answer the question
|
|
2145
|
-
* outright:
|
|
2146
|
-
*
|
|
2147
|
-
* - `gcMs` ≈ `stallMs` → garbage collection. Look at heap growth.
|
|
2148
|
-
* - `gcMs` ≈ 0 → something SYNCHRONOUS blocked the loop, and GC is innocent.
|
|
2149
|
-
*
|
|
2150
|
-
* Heap and RSS ride along because if it is GC, the next question is always
|
|
2151
|
-
* "growing since when?".
|
|
2152
|
-
*
|
|
2153
|
-
* Deliberately not a histogram (`monitorEventLoopDelay`): that reports a
|
|
2154
|
-
* distribution, and the question here is about ONE outlier and what coincided
|
|
2155
|
-
* with it.
|
|
2156
|
-
*/
|
|
2157
|
-
/** Tick period. Small enough to bound attribution error, large enough to cost
|
|
2158
|
-
* nothing on a process already doing frame work. */
|
|
2159
|
-
var STALL_TICK_MS = 1e3;
|
|
2160
|
-
/**
|
|
2161
|
-
* Report a freeze only past this. The runner's own inference deadline is 20s
|
|
2162
|
-
* and the observed freezes run 22-88s, so this is far below the signal while
|
|
2163
|
-
* staying clear of ordinary scheduling jitter (a busy frame path routinely
|
|
2164
|
-
* overruns a 1s timer by tens of ms).
|
|
2165
|
-
*/
|
|
2166
|
-
var STALL_REPORT_MS = 2e3;
|
|
2167
|
-
/**
|
|
2168
|
-
* Attribute a freeze. Pure, because this is the ONE judgement the whole module
|
|
2169
|
-
* exists to make and it must be testable without freezing a real event loop.
|
|
2170
|
-
*
|
|
2171
|
-
* `gc` when garbage collection accounts for most of the stall; a
|
|
2172
|
-
* `synchronous-block` when it accounts for almost none — that verdict points
|
|
2173
|
-
* away from the heap entirely and at whatever ran without yielding.
|
|
2174
|
-
*/
|
|
2175
|
-
function attributeStall(stallMs, gcMs, gcCount, longestGcMs) {
|
|
2176
|
-
const share = stallMs > 0 ? gcMs / stallMs : 0;
|
|
2177
|
-
return {
|
|
2178
|
-
stallMs,
|
|
2179
|
-
gcMs,
|
|
2180
|
-
gcCount,
|
|
2181
|
-
longestGcMs,
|
|
2182
|
-
cause: share >= .7 ? "gc" : share <= .2 ? "synchronous-block" : "mixed"
|
|
2183
|
-
};
|
|
2184
|
-
}
|
|
2185
|
-
/**
|
|
2186
|
-
* Start watching. Returns a stop function; safe to call twice.
|
|
2187
|
-
*
|
|
2188
|
-
* Never throws into the caller: a diagnostic that can take the detection
|
|
2189
|
-
* process down is worse than no diagnostic. `PerformanceObserver` for `gc` is
|
|
2190
|
-
* unavailable in some embedded runtimes, and that must degrade to
|
|
2191
|
-
* stall-detection-without-attribution rather than a boot failure.
|
|
2192
|
-
*/
|
|
2193
|
-
function startEventLoopStallMonitor(logger, reportThresholdMs = STALL_REPORT_MS) {
|
|
2194
|
-
let gc = {
|
|
2195
|
-
totalMs: 0,
|
|
2196
|
-
count: 0,
|
|
2197
|
-
longestMs: 0
|
|
2198
|
-
};
|
|
2199
|
-
let observer = null;
|
|
2200
|
-
try {
|
|
2201
|
-
observer = new PerformanceObserver((list) => {
|
|
2202
|
-
for (const entry of list.getEntries()) {
|
|
2203
|
-
gc.totalMs += entry.duration;
|
|
2204
|
-
gc.count += 1;
|
|
2205
|
-
if (entry.duration > gc.longestMs) gc.longestMs = entry.duration;
|
|
2206
|
-
}
|
|
2207
|
-
});
|
|
2208
|
-
observer.observe({ entryTypes: ["gc"] });
|
|
2209
|
-
} catch {
|
|
2210
|
-
observer = null;
|
|
2211
|
-
}
|
|
2212
|
-
let expected = Date.now() + STALL_TICK_MS;
|
|
2213
|
-
const timer = setInterval(() => {
|
|
2214
|
-
const now = Date.now();
|
|
2215
|
-
const lateBy = now - expected;
|
|
2216
|
-
const window = gc;
|
|
2217
|
-
gc = {
|
|
2218
|
-
totalMs: 0,
|
|
2219
|
-
count: 0,
|
|
2220
|
-
longestMs: 0
|
|
2221
|
-
};
|
|
2222
|
-
expected = now + STALL_TICK_MS;
|
|
2223
|
-
if (lateBy < reportThresholdMs) return;
|
|
2224
|
-
const verdict = attributeStall(lateBy, Math.round(window.totalMs), window.count, Math.round(window.longestMs));
|
|
2225
|
-
const mem = process.memoryUsage();
|
|
2226
|
-
logger.warn("event loop stalled", { meta: {
|
|
2227
|
-
stallMs: verdict.stallMs,
|
|
2228
|
-
cause: verdict.cause,
|
|
2229
|
-
gcMs: verdict.gcMs,
|
|
2230
|
-
gcCount: verdict.gcCount,
|
|
2231
|
-
longestGcMs: verdict.longestGcMs,
|
|
2232
|
-
gcObserved: observer !== null,
|
|
2233
|
-
heapUsedMb: Math.round(mem.heapUsed / 1e6),
|
|
2234
|
-
heapTotalMb: Math.round(mem.heapTotal / 1e6),
|
|
2235
|
-
rssMb: Math.round(mem.rss / 1e6),
|
|
2236
|
-
externalMb: Math.round(mem.external / 1e6),
|
|
2237
|
-
arrayBuffersMb: Math.round(mem.arrayBuffers / 1e6)
|
|
2238
|
-
} });
|
|
2239
|
-
}, STALL_TICK_MS);
|
|
2240
|
-
timer.unref?.();
|
|
2241
|
-
let stopped = false;
|
|
2242
|
-
return () => {
|
|
2243
|
-
if (stopped) return;
|
|
2244
|
-
stopped = true;
|
|
2245
|
-
clearInterval(timer);
|
|
2246
|
-
observer?.disconnect();
|
|
2247
|
-
};
|
|
2248
|
-
}
|
|
2249
|
-
//#endregion
|
|
2250
|
-
export { ALL_PIPELINE_STEPS as a, getDefaultModelForFormatFromDef as c, resolveModelForFormat as d, landmarkPrecisionVerdict as f, resolveFrameViewGeometry as i, getStep as l, startEventLoopStallMonitor as n, ALL_STEPS as o, localFrameRegistry as r, getDefaultModelForFormat as s, attributeStall as t, getStepDefinition as u };
|
|
2113
|
+
export { getDefaultModelForFormat as a, getStepDefinition as c, ALL_STEPS as i, resolveModelForFormat as l, resolveFrameViewGeometry as n, getDefaultModelForFormatFromDef as o, ALL_PIPELINE_STEPS as r, getStep as s, localFrameRegistry as t, landmarkPrecisionVerdict as u };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const require_dist = require("./dist-DML3mpn9.js");
|
|
2
2
|
let node_crypto = require("node:crypto");
|
|
3
|
-
let node_perf_hooks = require("node:perf_hooks");
|
|
4
3
|
//#region src/detection-pipeline/registry/model-catalogs.ts
|
|
5
4
|
var HF_REPO = "camstack/camstack-models";
|
|
6
5
|
var HF_SCRYPTED = "scrypted/plugin-models";
|
|
@@ -2111,142 +2110,6 @@ var localFrameRegistry = new LocalFrameRegistry({
|
|
|
2111
2110
|
maxBytes: envPositiveInt("CAMSTACK_LOCAL_FRAME_BUDGET_MB", DEFAULT_MAX_BYTES / 1048576) * 1048576
|
|
2112
2111
|
});
|
|
2113
2112
|
//#endregion
|
|
2114
|
-
//#region src/detection-pipeline/diagnostics/event-loop-stall-monitor.ts
|
|
2115
|
-
/**
|
|
2116
|
-
* event-loop-stall-monitor — names the thing that freezes the detection process.
|
|
2117
|
-
*
|
|
2118
|
-
* ## What this exists to settle
|
|
2119
|
-
*
|
|
2120
|
-
* On 2026-08-01/02 inference across the whole fleet stalled in bursts: 759
|
|
2121
|
-
* calls in 90 minutes crossed the runner's 20s deadline, p50 41s and max 80s,
|
|
2122
|
-
* and EVERY one of them eventually resolved. Six explanations were killed with
|
|
2123
|
-
* telemetry alone:
|
|
2124
|
-
*
|
|
2125
|
-
* - not a slow GPU or a wedged pool — the work completes, just enormously late;
|
|
2126
|
-
* - not CPU starvation — hosts sat at 8-48% during the freezes;
|
|
2127
|
-
* - not swap — `pswpin/pswpout` are flat zero on every host;
|
|
2128
|
-
* - not a logging artifact — Loki's timestamp trails the in-process one by
|
|
2129
|
-
* 0.1-1.0s at p95, so the gaps are real, not buffered delivery;
|
|
2130
|
-
* - not the v1.2.30 release — the stalls predate it by a day;
|
|
2131
|
-
* - not only the idle-pool recompile — that is real and hub-side, but the
|
|
2132
|
-
* agent stalls with no eviction and no compile at all.
|
|
2133
|
-
*
|
|
2134
|
-
* What IS established: the 10s stats timer — which touches no inference code —
|
|
2135
|
-
* stops firing for 22-88s and resumes at the same instant the abandoned calls
|
|
2136
|
-
* settle. The event loop itself freezes. From outside the process, GC and a
|
|
2137
|
-
* synchronous block are indistinguishable, and guessing between them is how
|
|
2138
|
-
* this investigation would go wrong. So measure it.
|
|
2139
|
-
*
|
|
2140
|
-
* ## How
|
|
2141
|
-
*
|
|
2142
|
-
* A 1s interval whose LATENESS is the freeze: a tick that arrives 40s late means
|
|
2143
|
-
* the loop was blocked ~39s, whatever blocked it. In the same window the GC
|
|
2144
|
-
* observer accumulates pause time. The two numbers together answer the question
|
|
2145
|
-
* outright:
|
|
2146
|
-
*
|
|
2147
|
-
* - `gcMs` ≈ `stallMs` → garbage collection. Look at heap growth.
|
|
2148
|
-
* - `gcMs` ≈ 0 → something SYNCHRONOUS blocked the loop, and GC is innocent.
|
|
2149
|
-
*
|
|
2150
|
-
* Heap and RSS ride along because if it is GC, the next question is always
|
|
2151
|
-
* "growing since when?".
|
|
2152
|
-
*
|
|
2153
|
-
* Deliberately not a histogram (`monitorEventLoopDelay`): that reports a
|
|
2154
|
-
* distribution, and the question here is about ONE outlier and what coincided
|
|
2155
|
-
* with it.
|
|
2156
|
-
*/
|
|
2157
|
-
/** Tick period. Small enough to bound attribution error, large enough to cost
|
|
2158
|
-
* nothing on a process already doing frame work. */
|
|
2159
|
-
var STALL_TICK_MS = 1e3;
|
|
2160
|
-
/**
|
|
2161
|
-
* Report a freeze only past this. The runner's own inference deadline is 20s
|
|
2162
|
-
* and the observed freezes run 22-88s, so this is far below the signal while
|
|
2163
|
-
* staying clear of ordinary scheduling jitter (a busy frame path routinely
|
|
2164
|
-
* overruns a 1s timer by tens of ms).
|
|
2165
|
-
*/
|
|
2166
|
-
var STALL_REPORT_MS = 2e3;
|
|
2167
|
-
/**
|
|
2168
|
-
* Attribute a freeze. Pure, because this is the ONE judgement the whole module
|
|
2169
|
-
* exists to make and it must be testable without freezing a real event loop.
|
|
2170
|
-
*
|
|
2171
|
-
* `gc` when garbage collection accounts for most of the stall; a
|
|
2172
|
-
* `synchronous-block` when it accounts for almost none — that verdict points
|
|
2173
|
-
* away from the heap entirely and at whatever ran without yielding.
|
|
2174
|
-
*/
|
|
2175
|
-
function attributeStall(stallMs, gcMs, gcCount, longestGcMs) {
|
|
2176
|
-
const share = stallMs > 0 ? gcMs / stallMs : 0;
|
|
2177
|
-
return {
|
|
2178
|
-
stallMs,
|
|
2179
|
-
gcMs,
|
|
2180
|
-
gcCount,
|
|
2181
|
-
longestGcMs,
|
|
2182
|
-
cause: share >= .7 ? "gc" : share <= .2 ? "synchronous-block" : "mixed"
|
|
2183
|
-
};
|
|
2184
|
-
}
|
|
2185
|
-
/**
|
|
2186
|
-
* Start watching. Returns a stop function; safe to call twice.
|
|
2187
|
-
*
|
|
2188
|
-
* Never throws into the caller: a diagnostic that can take the detection
|
|
2189
|
-
* process down is worse than no diagnostic. `PerformanceObserver` for `gc` is
|
|
2190
|
-
* unavailable in some embedded runtimes, and that must degrade to
|
|
2191
|
-
* stall-detection-without-attribution rather than a boot failure.
|
|
2192
|
-
*/
|
|
2193
|
-
function startEventLoopStallMonitor(logger, reportThresholdMs = STALL_REPORT_MS) {
|
|
2194
|
-
let gc = {
|
|
2195
|
-
totalMs: 0,
|
|
2196
|
-
count: 0,
|
|
2197
|
-
longestMs: 0
|
|
2198
|
-
};
|
|
2199
|
-
let observer = null;
|
|
2200
|
-
try {
|
|
2201
|
-
observer = new node_perf_hooks.PerformanceObserver((list) => {
|
|
2202
|
-
for (const entry of list.getEntries()) {
|
|
2203
|
-
gc.totalMs += entry.duration;
|
|
2204
|
-
gc.count += 1;
|
|
2205
|
-
if (entry.duration > gc.longestMs) gc.longestMs = entry.duration;
|
|
2206
|
-
}
|
|
2207
|
-
});
|
|
2208
|
-
observer.observe({ entryTypes: ["gc"] });
|
|
2209
|
-
} catch {
|
|
2210
|
-
observer = null;
|
|
2211
|
-
}
|
|
2212
|
-
let expected = Date.now() + STALL_TICK_MS;
|
|
2213
|
-
const timer = setInterval(() => {
|
|
2214
|
-
const now = Date.now();
|
|
2215
|
-
const lateBy = now - expected;
|
|
2216
|
-
const window = gc;
|
|
2217
|
-
gc = {
|
|
2218
|
-
totalMs: 0,
|
|
2219
|
-
count: 0,
|
|
2220
|
-
longestMs: 0
|
|
2221
|
-
};
|
|
2222
|
-
expected = now + STALL_TICK_MS;
|
|
2223
|
-
if (lateBy < reportThresholdMs) return;
|
|
2224
|
-
const verdict = attributeStall(lateBy, Math.round(window.totalMs), window.count, Math.round(window.longestMs));
|
|
2225
|
-
const mem = process.memoryUsage();
|
|
2226
|
-
logger.warn("event loop stalled", { meta: {
|
|
2227
|
-
stallMs: verdict.stallMs,
|
|
2228
|
-
cause: verdict.cause,
|
|
2229
|
-
gcMs: verdict.gcMs,
|
|
2230
|
-
gcCount: verdict.gcCount,
|
|
2231
|
-
longestGcMs: verdict.longestGcMs,
|
|
2232
|
-
gcObserved: observer !== null,
|
|
2233
|
-
heapUsedMb: Math.round(mem.heapUsed / 1e6),
|
|
2234
|
-
heapTotalMb: Math.round(mem.heapTotal / 1e6),
|
|
2235
|
-
rssMb: Math.round(mem.rss / 1e6),
|
|
2236
|
-
externalMb: Math.round(mem.external / 1e6),
|
|
2237
|
-
arrayBuffersMb: Math.round(mem.arrayBuffers / 1e6)
|
|
2238
|
-
} });
|
|
2239
|
-
}, STALL_TICK_MS);
|
|
2240
|
-
timer.unref?.();
|
|
2241
|
-
let stopped = false;
|
|
2242
|
-
return () => {
|
|
2243
|
-
if (stopped) return;
|
|
2244
|
-
stopped = true;
|
|
2245
|
-
clearInterval(timer);
|
|
2246
|
-
observer?.disconnect();
|
|
2247
|
-
};
|
|
2248
|
-
}
|
|
2249
|
-
//#endregion
|
|
2250
2113
|
Object.defineProperty(exports, "ALL_PIPELINE_STEPS", {
|
|
2251
2114
|
enumerable: true,
|
|
2252
2115
|
get: function() {
|
|
@@ -2259,12 +2122,6 @@ Object.defineProperty(exports, "ALL_STEPS", {
|
|
|
2259
2122
|
return ALL_STEPS;
|
|
2260
2123
|
}
|
|
2261
2124
|
});
|
|
2262
|
-
Object.defineProperty(exports, "attributeStall", {
|
|
2263
|
-
enumerable: true,
|
|
2264
|
-
get: function() {
|
|
2265
|
-
return attributeStall;
|
|
2266
|
-
}
|
|
2267
|
-
});
|
|
2268
2125
|
Object.defineProperty(exports, "getDefaultModelForFormat", {
|
|
2269
2126
|
enumerable: true,
|
|
2270
2127
|
get: function() {
|
|
@@ -2313,9 +2170,3 @@ Object.defineProperty(exports, "resolveModelForFormat", {
|
|
|
2313
2170
|
return resolveModelForFormat;
|
|
2314
2171
|
}
|
|
2315
2172
|
});
|
|
2316
|
-
Object.defineProperty(exports, "startEventLoopStallMonitor", {
|
|
2317
|
-
enumerable: true,
|
|
2318
|
-
get: function() {
|
|
2319
|
-
return startEventLoopStallMonitor;
|
|
2320
|
-
}
|
|
2321
|
-
});
|
|
@@ -4,11 +4,12 @@ Object.defineProperties(exports, {
|
|
|
4
4
|
});
|
|
5
5
|
const require_dist = require("../dist-DML3mpn9.js");
|
|
6
6
|
const require_node = require("../node-OvVqe9eh.js");
|
|
7
|
-
const
|
|
7
|
+
const require_local_frame_registry = require("../local-frame-registry-DO8-LuSo.js");
|
|
8
8
|
const require_worker_protocol = require("../worker-protocol-Dt3zWCQY.js");
|
|
9
9
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
10
10
|
const require_lazy_sharp = require("../lazy-sharp-BzXIjfN8.js");
|
|
11
11
|
const require_restream_intent = require("../restream-intent-Cv9x3jmu.js");
|
|
12
|
+
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaUBcb13.js");
|
|
12
13
|
const require_remote_restream = require("../remote-restream-CO36Sr30.js");
|
|
13
14
|
let node_child_process = require("node:child_process");
|
|
14
15
|
let sharp = require("sharp");
|
|
@@ -1408,7 +1409,7 @@ function createSessionDecodeCoordinator(deps) {
|
|
|
1408
1409
|
* shm-ring cameras.
|
|
1409
1410
|
*/
|
|
1410
1411
|
async function toDecodedFrame(frame, format, viewSpec) {
|
|
1411
|
-
const geometry = viewSpec ?
|
|
1412
|
+
const geometry = viewSpec ? require_local_frame_registry.resolveFrameViewGeometry(frame.image.width, frame.image.height, viewSpec) : void 0;
|
|
1412
1413
|
const bytes = await frame.image.toBuffer(geometry ? {
|
|
1413
1414
|
crop: geometry.sourceCrop,
|
|
1414
1415
|
resize: geometry.output,
|
|
@@ -5056,7 +5057,7 @@ function writeRetainedDetectionFrame(store, deviceId, frame, retention) {
|
|
|
5056
5057
|
if (retention) return retention.admitFrame(deviceId, frame);
|
|
5057
5058
|
if (!store) return void 0;
|
|
5058
5059
|
if (!isContinuousRetainEnabled()) return store.allocateHandle(deviceId, frame) ?? void 0;
|
|
5059
|
-
return store.write(deviceId, frame, frame.frameRef ? createRetainedContentFallback(
|
|
5060
|
+
return store.write(deviceId, frame, frame.frameRef ? createRetainedContentFallback(require_local_frame_registry.localFrameRegistry, frame.frameRef) : void 0) ?? void 0;
|
|
5060
5061
|
}
|
|
5061
5062
|
var DETECTION_KEEPWARM_MS = 15e3;
|
|
5062
5063
|
/**
|
|
@@ -5117,7 +5118,7 @@ function collectEnabledStepIds(steps, acc) {
|
|
|
5117
5118
|
*/
|
|
5118
5119
|
function tryGetStepDefinition(stepId) {
|
|
5119
5120
|
try {
|
|
5120
|
-
return
|
|
5121
|
+
return require_local_frame_registry.getStepDefinition(stepId);
|
|
5121
5122
|
} catch {
|
|
5122
5123
|
return null;
|
|
5123
5124
|
}
|
|
@@ -5576,7 +5577,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
5576
5577
|
});
|
|
5577
5578
|
this.retention = new OnDemandRetentionSession({
|
|
5578
5579
|
store: this.retainedFrames,
|
|
5579
|
-
registry:
|
|
5580
|
+
registry: require_local_frame_registry.localFrameRegistry,
|
|
5580
5581
|
telemetry: this.frameLazy,
|
|
5581
5582
|
nodeId: this.nodeId,
|
|
5582
5583
|
continuousRetain: isContinuousRetainEnabled()
|
|
@@ -5588,7 +5589,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
5588
5589
|
minThrottledFps: this.config.minThrottledFps,
|
|
5589
5590
|
processFrame: (deviceId, frame, handle) => this.runInference(deviceId, frame, handle),
|
|
5590
5591
|
analyzeMotion: (deviceId, frame, handle) => this.runMotionAnalysis(deviceId, frame, handle),
|
|
5591
|
-
releaseFrameRef: (ref, reason) =>
|
|
5592
|
+
releaseFrameRef: (ref, reason) => require_local_frame_registry.localFrameRegistry.release(ref, reason),
|
|
5592
5593
|
releaseRetention: (handle, reason) => this.retention?.release(handle, reason),
|
|
5593
5594
|
hydrateRetainedFrame: (handle) => this.retainedFrames?.ensureContent(handle) ?? Promise.resolve(null),
|
|
5594
5595
|
onPhaseChanged: (deviceId, phase, meta) => this.handlePhaseChanged(deviceId, phase, meta),
|
|
@@ -7424,7 +7425,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7424
7425
|
return materialize;
|
|
7425
7426
|
},
|
|
7426
7427
|
...useLocalFrameRef ? {
|
|
7427
|
-
frameRegistry:
|
|
7428
|
+
frameRegistry: require_local_frame_registry.localFrameRegistry,
|
|
7428
7429
|
onFrameRef: async (frame, nativeCrop) => {
|
|
7429
7430
|
const current = reservation;
|
|
7430
7431
|
reservation = null;
|
|
@@ -7441,7 +7442,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
7441
7442
|
});
|
|
7442
7443
|
const admitted = current.detection && runner.enqueueReservedDetectionFrame(config.deviceId, frame, handle);
|
|
7443
7444
|
if (current.motion) {
|
|
7444
|
-
const motion = await resolveMotionFrameView(
|
|
7445
|
+
const motion = await resolveMotionFrameView(require_local_frame_registry.localFrameRegistry, frame.frameRef);
|
|
7445
7446
|
if (motion.kind === "view") runner.enqueueMotionFrame(config.deviceId, {
|
|
7446
7447
|
data: Buffer.from(motion.data),
|
|
7447
7448
|
width: motion.width,
|
|
@@ -8106,7 +8107,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
|
|
|
8106
8107
|
format: "rgb"
|
|
8107
8108
|
},
|
|
8108
8109
|
...useLocalFrameRef ? {
|
|
8109
|
-
frameRegistry:
|
|
8110
|
+
frameRegistry: require_local_frame_registry.localFrameRegistry,
|
|
8110
8111
|
onFrameRef: (frame, nativeCrop) => {
|
|
8111
8112
|
if (settled) return false;
|
|
8112
8113
|
const handle = writeRetainedDetectionFrame(retainedFrames, config.deviceId, frame, this.retention);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { B as defineCustomActions, Bt as nodePin, Ft as createEvent, Gt as _enum, H as deriveDetailCropRect, Kt as array, L as customAction, Qt as object, X as failureContributionCapability, Xt as literal, Yt as lazy, Zt as number, _ as FailureCounters, en as string, et as loadContributionCapability, ft as pipelineRunnerCapability, jt as BaseAddon, lt as pickDetailCropConvention, m as DEVICE_BACKEND_TO_FORMAT, nn as EventCategory, p as DEFAULT_NATIVE_LEASE_SETTINGS, qt as boolean, u as DEFAULT_DETAIL_CROP_CONVENTION, ut as pickNativeLeaseOverride, wt as errMsg } from "../dist-cZopp8Xm.mjs";
|
|
2
2
|
import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-Br6p9KvD.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { c as getStepDefinition, n as resolveFrameViewGeometry, t as localFrameRegistry } from "../local-frame-registry-Ctv_TzmF.mjs";
|
|
4
4
|
import { a as nativeLeaseSettingEnv, t as isWorkerReply } from "../worker-protocol-CUCQ1IPR.mjs";
|
|
5
5
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
6
6
|
import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
|
|
7
7
|
import { n as withDetectionIntent } from "../restream-intent-B4BXZra7.mjs";
|
|
8
|
+
import { t as attributeStall } from "../event-loop-stall-monitor-DXLNBMkY.mjs";
|
|
8
9
|
import { n as profileForStreamId, t as isRemoteRestream } from "../remote-restream-BeHi78PZ.mjs";
|
|
9
10
|
import { fork } from "node:child_process";
|
|
10
11
|
import sharp from "sharp";
|
package/dist/recorder/index.js
CHANGED
|
@@ -2,6 +2,7 @@ const require_dist = require("../dist-DML3mpn9.js");
|
|
|
2
2
|
const require_node = require("../node-OvVqe9eh.js");
|
|
3
3
|
const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
|
|
4
4
|
const require_restream_intent = require("../restream-intent-Cv9x3jmu.js");
|
|
5
|
+
const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaUBcb13.js");
|
|
5
6
|
const require_retire_root_keys = require("../retire-root-keys-KE6D6Xh_.js");
|
|
6
7
|
let node_crypto = require("node:crypto");
|
|
7
8
|
let node_fs = require("node:fs");
|
|
@@ -13365,6 +13366,16 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
13365
13366
|
isRecordingNode = true;
|
|
13366
13367
|
index = new RecordingIndex();
|
|
13367
13368
|
/**
|
|
13369
|
+
* Stops the GC-attributing stall monitor armed in {@link onInitialize}.
|
|
13370
|
+
*
|
|
13371
|
+
* REUSED, not re-implemented, and not promoted: `startEventLoopStallMonitor`
|
|
13372
|
+
* already ships inside `@camstack/addon-pipeline` — the same bundle as this
|
|
13373
|
+
* addon — and moving it into `@camstack/system` would duplicate a mechanism
|
|
13374
|
+
* that already exists (forbidden) or relocate it across a package boundary,
|
|
13375
|
+
* which is a refactor with a slower ship chain, not a measurement.
|
|
13376
|
+
*/
|
|
13377
|
+
stopStallMonitor = null;
|
|
13378
|
+
/**
|
|
13368
13379
|
* Durable hour-directory copy of {@link index}. Null on an inert
|
|
13369
13380
|
* (non-recording) node. Boot reseeds the RAM index from this; the
|
|
13370
13381
|
* deferred archive walk reconciles it against disk.
|
|
@@ -13532,6 +13543,7 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
13532
13543
|
} });
|
|
13533
13544
|
}
|
|
13534
13545
|
async onInitialize() {
|
|
13546
|
+
this.stopStallMonitor = require_event_loop_stall_monitor.startEventLoopStallMonitor(this.ctx.logger);
|
|
13535
13547
|
const raw = this.ctx.kernel.localNodeId ?? this.ctx.id;
|
|
13536
13548
|
this.nodeId = raw.includes("/") ? raw.split("/")[0] : raw;
|
|
13537
13549
|
const configuredNode = this.config.recordingNodeId;
|
|
@@ -13971,6 +13983,8 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
|
|
|
13971
13983
|
};
|
|
13972
13984
|
}
|
|
13973
13985
|
async onShutdown() {
|
|
13986
|
+
this.stopStallMonitor?.();
|
|
13987
|
+
this.stopStallMonitor = null;
|
|
13974
13988
|
this.eventUnsub?.();
|
|
13975
13989
|
this.eventUnsub = null;
|
|
13976
13990
|
this.audioTriggerUnsub?.();
|
package/dist/recorder/index.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { $ as isFirstLevelMacroClass, $t as record, B as defineCustomActions, Bt
|
|
|
2
2
|
import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-Br6p9KvD.mjs";
|
|
3
3
|
import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
|
|
4
4
|
import { r as withRecordingIntent } from "../restream-intent-B4BXZra7.mjs";
|
|
5
|
+
import { n as startEventLoopStallMonitor } from "../event-loop-stall-monitor-DXLNBMkY.mjs";
|
|
5
6
|
import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DMolfhsP.mjs";
|
|
6
7
|
import { randomUUID } from "node:crypto";
|
|
7
8
|
import { createReadStream, promises, realpathSync } from "node:fs";
|
|
@@ -13363,6 +13364,16 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
13363
13364
|
isRecordingNode = true;
|
|
13364
13365
|
index = new RecordingIndex();
|
|
13365
13366
|
/**
|
|
13367
|
+
* Stops the GC-attributing stall monitor armed in {@link onInitialize}.
|
|
13368
|
+
*
|
|
13369
|
+
* REUSED, not re-implemented, and not promoted: `startEventLoopStallMonitor`
|
|
13370
|
+
* already ships inside `@camstack/addon-pipeline` — the same bundle as this
|
|
13371
|
+
* addon — and moving it into `@camstack/system` would duplicate a mechanism
|
|
13372
|
+
* that already exists (forbidden) or relocate it across a package boundary,
|
|
13373
|
+
* which is a refactor with a slower ship chain, not a measurement.
|
|
13374
|
+
*/
|
|
13375
|
+
stopStallMonitor = null;
|
|
13376
|
+
/**
|
|
13366
13377
|
* Durable hour-directory copy of {@link index}. Null on an inert
|
|
13367
13378
|
* (non-recording) node. Boot reseeds the RAM index from this; the
|
|
13368
13379
|
* deferred archive walk reconciles it against disk.
|
|
@@ -13530,6 +13541,7 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
13530
13541
|
} });
|
|
13531
13542
|
}
|
|
13532
13543
|
async onInitialize() {
|
|
13544
|
+
this.stopStallMonitor = startEventLoopStallMonitor(this.ctx.logger);
|
|
13533
13545
|
const raw = this.ctx.kernel.localNodeId ?? this.ctx.id;
|
|
13534
13546
|
this.nodeId = raw.includes("/") ? raw.split("/")[0] : raw;
|
|
13535
13547
|
const configuredNode = this.config.recordingNodeId;
|
|
@@ -13969,6 +13981,8 @@ var RecorderV2Addon = class extends BaseAddon {
|
|
|
13969
13981
|
};
|
|
13970
13982
|
}
|
|
13971
13983
|
async onShutdown() {
|
|
13984
|
+
this.stopStallMonitor?.();
|
|
13985
|
+
this.stopStallMonitor = null;
|
|
13972
13986
|
this.eventUnsub?.();
|
|
13973
13987
|
this.eventUnsub = null;
|
|
13974
13988
|
this.audioTriggerUnsub?.();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-pipeline",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.146",
|
|
4
4
|
"description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|