first commit
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# `WorkerManager`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`WorkerManager` is the public entry point for distributing tasks across a `WorkerPool`. It owns task assignment (load-balancing, retry, dead-letter), supervision (periodic health checks, auto-restart of unhealthy workers), and option lifecycle (configurable at construction and at runtime via `updateOptions`). Implementation details — the retry-with-backoff loop and the supervisor — live in `manager-dispatch.ts` and `manager-supervisor.ts` so this file stays focused on the surface contract.
|
||||
|
||||
## Features
|
||||
|
||||
- `assignTask<I, O>(task, input, signal?): Promise<O>` — pick a worker, execute, retry up to `maxTaskRetries`, record dead-letter on exhaustion.
|
||||
- `assignTasks<I, O>(tasks[], inputs[], signal?)` — fan-out wrapper over `assignTask`.
|
||||
- Auto health-check loop driven by `setInterval` over `healthCheckIntervalMs`; toggleable at runtime.
|
||||
- Auto-restart of `UNHEALTHY` workers up to `maxWorkerRestarts`.
|
||||
- `failedTasks` Set and per-task retry counter for observability.
|
||||
- Injectable `LoadBalancingStrategy` (default `RoundRobinStrategy`), `WorkerHealthChecker` (default `PeriodicHealthChecker`), `Clock`, `LoggerLike`.
|
||||
- AbortSignal threaded through every public async method.
|
||||
- Throws `InvalidOptionsError` for negative numeric options at construction and on `updateOptions`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { WorkerManager } from "@suaveplan/queue-distribution/manager/manager";
|
||||
import { WorkerPool } from "@suaveplan/queue-distribution/pool/pool";
|
||||
import { LocalWorker } from "@suaveplan/queue-distribution/worker/local/local";
|
||||
|
||||
const pool = new WorkerPool();
|
||||
pool.addWorker(new LocalWorker({}, { id: "a" }));
|
||||
const manager = new WorkerManager(pool, {
|
||||
autoHealthCheck: false,
|
||||
maxTaskRetries: 2,
|
||||
});
|
||||
|
||||
const result = await manager.assignTask(task, input);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
Stand up a production-shaped manager with a least-busy strategy, performance bias, a custom health checker, and a deterministic clock for tests.
|
||||
|
||||
```ts
|
||||
import { WorkerManager } from "@suaveplan/queue-distribution/manager/manager";
|
||||
import { WorkerPool } from "@suaveplan/queue-distribution/pool/pool";
|
||||
import { LeastBusyStrategy } from "@suaveplan/queue-distribution/load-balancing/least-busy/least-busy";
|
||||
import { PeriodicHealthChecker } from "@suaveplan/queue-distribution/health-checker/health-checker";
|
||||
|
||||
const clock = (): number => Date.now();
|
||||
const pool = new WorkerPool();
|
||||
// ... add workers ...
|
||||
|
||||
const manager = new WorkerManager(
|
||||
pool,
|
||||
{
|
||||
balancer: new LeastBusyStrategy({ usePerformanceMetrics: true }),
|
||||
healthChecker: new PeriodicHealthChecker({ unhealthyThreshold: 3 }),
|
||||
autoHealthCheck: true,
|
||||
healthCheckIntervalMs: 10_000,
|
||||
maxTaskRetries: 5,
|
||||
taskRetryDelayMs: 250,
|
||||
autoRestartUnhealthy: true,
|
||||
maxWorkerRestarts: 5,
|
||||
minOnlineWorkers: 2,
|
||||
},
|
||||
{ clock, logger: console as never },
|
||||
);
|
||||
|
||||
const ctrl = new AbortController();
|
||||
setTimeout(() => ctrl.abort(), 30_000); // hard ceiling
|
||||
await manager.assignTasks(tasks, inputs, ctrl.signal);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `interface WorkerManagerOptions` — `healthCheckIntervalMs?`, `autoHealthCheck?`, `taskRetryDelayMs?`, `maxTaskRetries?`, `autoRestartUnhealthy?`, `maxWorkerRestarts?`, `minOnlineWorkers?`.
|
||||
- `interface WorkerManagerDeps` — `clock?`, `logger?`.
|
||||
- `class WorkerManager`.
|
||||
- `constructor(pool, options?: { balancer?, healthChecker?, ...WorkerManagerOptions }, deps?: WorkerManagerDeps)`.
|
||||
- `assignTask<I, O>(task, input, signal?): Promise<O>`.
|
||||
- `assignTasks<I, O>(tasks[], inputs[], signal?): Promise<O[]>`.
|
||||
- `startHealthChecks(): void`, `stopHealthChecks(): void`.
|
||||
- `checkAllWorkersHealth(signal?): Promise<Map<string, WorkerHealth>>`.
|
||||
- `handleWorkerFailure(worker, error): Promise<void>`.
|
||||
- `getWorkerPool(): WorkerPool`, `getLoadBalancer(): LoadBalancingStrategy`, `setLoadBalancer(b): void`, `getHealthChecker(): WorkerHealthChecker`.
|
||||
- `getFailedTasks(): ReadonlySet<DistributedTask>`, `clearFailedTasks(): void`.
|
||||
- `getTaskRetries(taskId): number`.
|
||||
- `getOptions(): Required<WorkerManagerOptions>`, `updateOptions(partial): void`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`updateOptions` performs validate-then-apply: a negative `maxTaskRetries` is rejected before any mutation, so the manager remains in a consistent state across failed updates. Toggling `autoHealthCheck` mid-run actually starts or stops the interval — passing the same value as the current state is a no-op, which lets callers idempotently bounce settings without disrupting an active loop. The dispatch and supervisor configs are recreated on each call to `assignTask` / `checkAllWorkersHealth` so changes via `setLoadBalancer` are picked up immediately without rebuilding the manager.
|
||||
Reference in New Issue
Block a user