batch-cluster
Version:
Manage a cluster of child processes
163 lines • 6.47 kB
JavaScript
"use strict";
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _ProcessHealthMonitor_healthCheckStates;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProcessHealthMonitor = void 0;
const HealthCheckStrategy_1 = require("./HealthCheckStrategy");
const Parser_1 = require("./Parser");
const String_1 = require("./String");
const Task_1 = require("./Task");
/**
* Manages health checking logic for processes.
* Provides centralized health assessment and monitoring capabilities.
*/
class ProcessHealthMonitor {
constructor(options, emitter, healthStrategy) {
this.options = options;
this.emitter = emitter;
_ProcessHealthMonitor_healthCheckStates.set(this, new Map());
this.healthStrategy = healthStrategy !== null && healthStrategy !== void 0 ? healthStrategy : new HealthCheckStrategy_1.CompositeHealthCheckStrategy();
}
/**
* Initialize health monitoring for a process
*/
initializeProcess(pid) {
__classPrivateFieldGet(this, _ProcessHealthMonitor_healthCheckStates, "f").set(pid, {
lastHealthCheck: Date.now(),
healthCheckFailures: 0,
lastJobFailed: false,
});
}
/**
* Clean up health monitoring for a process
*/
cleanupProcess(pid) {
__classPrivateFieldGet(this, _ProcessHealthMonitor_healthCheckStates, "f").delete(pid);
}
/**
* Record that a job failed for a process
*/
recordJobFailure(pid) {
const state = __classPrivateFieldGet(this, _ProcessHealthMonitor_healthCheckStates, "f").get(pid);
if (state != null) {
state.lastJobFailed = true;
}
}
/**
* Record that a job succeeded for a process
*/
recordJobSuccess(pid) {
const state = __classPrivateFieldGet(this, _ProcessHealthMonitor_healthCheckStates, "f").get(pid);
if (state != null) {
state.lastJobFailed = false;
}
}
/**
* Assess the health of a process and return why it's not healthy, or null if healthy
*/
assessHealth(process, overrideReason) {
if (overrideReason != null)
return overrideReason;
const state = __classPrivateFieldGet(this, _ProcessHealthMonitor_healthCheckStates, "f").get(process.pid);
if (state != null && state.healthCheckFailures > 0) {
return "unhealthy";
}
return this.healthStrategy.assess(process, this.options);
}
/**
* Check if a process is healthy
*/
isHealthy(process, overrideReason) {
return this.assessHealth(process, overrideReason) == null;
}
/**
* Assess why a process is not ready (combines health and business)
*/
assessReadiness(process, overrideReason) {
return !process.idle ? "busy" : this.assessHealth(process, overrideReason);
}
/**
* Check if a process is ready to handle tasks
*/
isReady(process, overrideReason) {
return this.assessReadiness(process, overrideReason) == null;
}
/**
* Run a health check on a process if needed
*/
maybeRunHealthcheck(process) {
const hcc = this.options.healthCheckCommand;
// if there's no health check command, no-op.
if (hcc == null || (0, String_1.blank)(hcc))
return;
// if the prior health check failed, .ready will be false
if (!this.isReady(process))
return;
const state = __classPrivateFieldGet(this, _ProcessHealthMonitor_healthCheckStates, "f").get(process.pid);
if (state == null)
return;
if (state.lastJobFailed ||
(this.options.healthCheckIntervalMillis > 0 &&
Date.now() - state.lastHealthCheck >
this.options.healthCheckIntervalMillis)) {
state.lastHealthCheck = Date.now();
const t = new Task_1.Task(hcc, Parser_1.SimpleParser);
t.promise
.catch((err) => {
this.emitter.emit("healthCheckError", err instanceof Error ? err : new Error(String(err)),
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
process);
state.healthCheckFailures++;
// BatchCluster will see we're unhealthy and reap us later
})
.finally(() => {
state.lastHealthCheck = Date.now();
});
// Execute the health check task on the process
if (process.execTask(t)) {
return t;
}
}
return;
}
/**
* Get health statistics for monitoring
*/
getHealthStats() {
let totalFailures = 0;
let processesWithFailures = 0;
for (const state of __classPrivateFieldGet(this, _ProcessHealthMonitor_healthCheckStates, "f").values()) {
totalFailures += state.healthCheckFailures;
if (state.healthCheckFailures > 0) {
processesWithFailures++;
}
}
return {
monitoredProcesses: __classPrivateFieldGet(this, _ProcessHealthMonitor_healthCheckStates, "f").size,
totalHealthCheckFailures: totalFailures,
processesWithFailures,
};
}
/**
* Reset health check failures for a process (useful for recovery scenarios)
*/
resetHealthCheckFailures(pid) {
const state = __classPrivateFieldGet(this, _ProcessHealthMonitor_healthCheckStates, "f").get(pid);
if (state != null) {
state.healthCheckFailures = 0;
}
}
/**
* Get health check state for a specific process
*/
getProcessHealthState(pid) {
return __classPrivateFieldGet(this, _ProcessHealthMonitor_healthCheckStates, "f").get(pid);
}
}
exports.ProcessHealthMonitor = ProcessHealthMonitor;
_ProcessHealthMonitor_healthCheckStates = new WeakMap();
//# sourceMappingURL=ProcessHealthMonitor.js.map