# `WorkerManager` _(no submodules)_ ## 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(task, input, signal?): Promise` — pick a worker, execute, retry up to `maxTaskRetries`, record dead-letter on exhaustion. - `assignTasks(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(task, input, signal?): Promise`. - `assignTasks(tasks[], inputs[], signal?): Promise`. - `startHealthChecks(): void`, `stopHealthChecks(): void`. - `checkAllWorkersHealth(signal?): Promise>`. - `handleWorkerFailure(worker, error): Promise`. - `getWorkerPool(): WorkerPool`, `getLoadBalancer(): LoadBalancingStrategy`, `setLoadBalancer(b): void`, `getHealthChecker(): WorkerHealthChecker`. - `getFailedTasks(): ReadonlySet`, `clearFailedTasks(): void`. - `getTaskRetries(taskId): number`. - `getOptions(): Required`, `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.