Files
skills/docs/foundation/data/web-workers/src/worker-pool/scheduler.md
2026-05-29 08:20:56 +01:00

2.6 KiB
Raw Blame History

scheduler

Purpose

scheduler.ts exposes pickSlot, the pure function WorkerPool calls to decide which slot receives the next submit. The function takes a projection of the pool's slots (only the data the scheduler needs: name + pendingCount) and the strategy name, and returns the chosen slot plus the next round-robin index.

Isolating the scheduling decision in a pure function makes it independently testable, swappable (a future scheduler can be added without touching the pool), and free of side effects.

Features

  • Two scheduling strategies: round-robin and least-busy.
  • SchedulerSlot projection — lightweight view of the pool's slots so the scheduler doesn't depend on WorkerPool's internals.
  • Returns undefined when the slot list is empty, letting the caller decide what to do (typically reject with RESTART_EXHAUSTED).
  • Round-robin returns the next index so the caller can persist it.

Usage: Basic Example

import { pickSlot } from "@suaveplan/web-workers";

const slots = [
    { name: "w0", pendingCount: 0 },
    { name: "w1", pendingCount: 0 },
];
const pick = pickSlot("round-robin", slots, 0);
// pick.slot.name === "w0", pick.nextIndex === 1

Usage: Advanced Example

import {
    pickSlot,
    type SchedulerSlot,
    type SchedulerStrategy,
} from "@suaveplan/web-workers";

class CustomPool {
    private slots: Array<{ name: string; pendingCount: number }> = [];
    private nextRR = 0;
    pick(strategy: SchedulerStrategy): SchedulerSlot | undefined {
        const projection = this.slots.map((s) => ({
            name: s.name,
            pendingCount: s.pendingCount,
        }));
        const result = pickSlot(strategy, projection, this.nextRR);
        if (result === undefined) return undefined;
        this.nextRR = result.nextIndex;
        return result.slot;
    }
}

API Reference

  • type SchedulerStrategy = "round-robin" | "least-busy".
  • interface SchedulerSlot { name: string; pendingCount: number }.
  • pickSlot(strategy, slots, nextRoundRobinIndex): { slot, nextIndex } | undefined.

Implementation Notes

round-robin uses nextRoundRobinIndex % slots.length so the caller can keep a monotonically-increasing counter without worrying about wraparound. least-busy linear-scans the slot list; ties go to the lowest-index slot (deterministic). The pure-function shape means the same slot projection always returns the same result — no hidden state, no randomness. For large pools (> 100 slots) a heap-based least-busy could be faster, but the typical pool size (4–16 slots) makes the linear scan negligible.