first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
# `WorkerMetricsState` helpers
## Purpose
`abstract-metrics.ts` carries the pure functions and the small accumulator used by `AbstractWorker` to track success/failure counts and average response time. Splitting them out of `abstract.ts` keeps each file under the 300-LOC budget, makes the bookkeeping unit-testable in isolation, and lets a subclass reuse the same helpers if it overrides `getHealth` with additional fields layered on top of the base payload.
## Features
- `createMetricsState()` — fresh `WorkerMetricsState` (`tasksSucceeded`, `tasksFailed`, `totalResponseTime`, `totalTasksCompleted`).
- `recordSuccess(state, responseTimeMs)` — increment successes, accumulate response time.
- `recordFailure(state)` — increment failures only (response time is undefined for failures).
- `buildDefaultHealth(state, { activeTasks, isHealthy })` — synthesise the default `WorkerHealth` payload using the accumulator's totals.
- CPU / memory in the synthesised payload scale linearly with `activeTasks` and cap at 100 — purely synthetic, intended as a placeholder until subclasses inject real measurements.
## Usage: Basic Example
```ts
import {
createMetricsState,
recordSuccess,
recordFailure,
buildDefaultHealth,
} from "@suaveplan/queue-distribution/worker/abstract/abstract-metrics";
const state = createMetricsState();
recordSuccess(state, 120);
recordSuccess(state, 80);
recordFailure(state);
const health = buildDefaultHealth(state, { activeTasks: 1, isHealthy: true });
// health.metrics.responseTime === 100
// health.metrics.tasksSucceeded === 2
// health.metrics.tasksFailed === 1
```
## Usage: Advanced Example
A subclass can compose its own metrics on top of the default payload — for example, a remote worker that wants to pass through CPU / memory from the upstream server while keeping the synthesised counters.
```ts
import { AbstractWorker } from "@suaveplan/queue-distribution/worker/abstract/abstract";
import { buildDefaultHealth } from "@suaveplan/queue-distribution/worker/abstract/abstract-metrics";
import type { WorkerHealth } from "@suaveplan/queue-distribution/worker/worker";
class RemoteShim extends AbstractWorker {
override async getHealth(): Promise<WorkerHealth> {
const base = buildDefaultHealth(
(this as unknown as { metrics: ReturnType<typeof createMetricsState> }).metrics,
{ activeTasks: this.activeTasks, isHealthy: this.status !== "OFFLINE" },
);
const upstream = await fetchUpstreamHealth();
return {
isHealthy: base.isHealthy && upstream.ok,
metrics: { ...base.metrics, cpu: upstream.cpu, memory: upstream.memory },
};
}
protected async doExecuteTask<I, O>(t, i): Promise<O> {
return undefined as never;
}
protected getType(): string {
return "shim";
}
}
```
## API Reference
- `interface WorkerMetricsState` — `tasksSucceeded`, `tasksFailed`, `totalResponseTime`, `totalTasksCompleted` (all numbers).
- `function createMetricsState(): WorkerMetricsState`.
- `function recordSuccess(state, responseTimeMs): void`.
- `function recordFailure(state): void`.
- `function buildDefaultHealth(state, { activeTasks, isHealthy }): WorkerHealth`.
## Implementation Notes
The CPU / memory placeholders (`activeTasks * 25`, `activeTasks * 15`, capped at 100) are explicitly synthetic — the upstream library returned the same shape and downstream consumers may have come to expect a non-zero CPU value when the worker is doing anything. Subclasses that have access to real OS-level metrics should override `getHealth()` end-to-end rather than calling `buildDefaultHealth` first; the helper exists so subclasses can layer on top without losing the success/failure counters.