logos-delivery-js/packages/core/src/lib/health_manager.ts
Danish Arora d464af3645
feat: node and protocols health (#2080)
* feat: introduce HealthManager

* feat: make health accessible on Waku object

* feat: update health from protocols

* chore: add access modifiers to healthmanager

* feat: use a HealthManager singleton

* chore: add tests for Filter, LightPush and Store

* feat: add overall node health

* chore: update protocol health to consider Store protocol

* chore: setup generic test utils instead of using filter utils

* tests: add a health status matrix check from 0-3

* chore: increase timeout for failing tests in CI
tests pass locally without an increased timeout, but fail in CI

* chore: move name inference to HealthManager

* tests: abstract away node creation and teardown utils

* fix: import
2024-07-27 18:27:54 +05:30

91 lines
2.4 KiB
TypeScript

import {
HealthStatus,
type IHealthManager,
NodeHealth,
type ProtocolHealth,
Protocols
} from "@waku/interfaces";
class HealthManager implements IHealthManager {
public static instance: HealthManager;
private readonly health: NodeHealth;
private constructor() {
this.health = {
overallStatus: HealthStatus.Unhealthy,
protocolStatuses: new Map()
};
}
public static getInstance(): HealthManager {
if (!HealthManager.instance) {
HealthManager.instance = new HealthManager();
}
return HealthManager.instance;
}
public getHealthStatus(): HealthStatus {
return this.health.overallStatus;
}
public getProtocolStatus(protocol: Protocols): ProtocolHealth | undefined {
return this.health.protocolStatuses.get(protocol);
}
public updateProtocolHealth(
multicodec: string,
connectedPeers: number
): void {
const protocol = this.getNameFromMulticodec(multicodec);
let status: HealthStatus = HealthStatus.Unhealthy;
if (connectedPeers == 1) {
status = HealthStatus.MinimallyHealthy;
} else if (connectedPeers >= 2) {
status = HealthStatus.SufficientlyHealthy;
}
this.health.protocolStatuses.set(protocol, {
name: protocol,
status: status,
lastUpdate: new Date()
});
this.updateOverallHealth();
}
private getNameFromMulticodec(multicodec: string): Protocols {
let name: Protocols;
if (multicodec.includes("filter")) {
name = Protocols.Filter;
} else if (multicodec.includes("lightpush")) {
name = Protocols.LightPush;
} else if (multicodec.includes("store")) {
name = Protocols.Store;
} else {
throw new Error(`Unknown protocol: ${multicodec}`);
}
return name;
}
private updateOverallHealth(): void {
const relevantProtocols = [Protocols.LightPush, Protocols.Filter];
const statuses = relevantProtocols.map(
(p) => this.getProtocolStatus(p)?.status
);
if (statuses.some((status) => status === HealthStatus.Unhealthy)) {
this.health.overallStatus = HealthStatus.Unhealthy;
} else if (
statuses.some((status) => status === HealthStatus.MinimallyHealthy)
) {
this.health.overallStatus = HealthStatus.MinimallyHealthy;
} else {
this.health.overallStatus = HealthStatus.SufficientlyHealthy;
}
}
}
export const getHealthManager = (): HealthManager =>
HealthManager.getInstance();