# `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 { const base = buildDefaultHealth( (this as unknown as { metrics: ReturnType }).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(t, i): Promise { 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.