Files
skills/docs/foundation/flow/queue-distribution/src/manager/manager.md
2026-05-29 08:20:56 +01:00

4.7 KiB

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<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

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.

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.