EnglishGitHub
Prototyppackages/worker-pool/src

Worker Pool

@ralphschuler/worker-pool

Führt vertrauenswürdige eigenständige Funktionen auf begrenzten Workern mit Timeout-Erholung aus.

worker_threadsparallelismbackpressure

01 · Problem

Wofür braucht man das?

CPU-intensive Funktionen blockieren den Event Loop; pro Aufgabe einen neuen Thread zu starten ist jedoch teuer und braucht keine kontrollierte Warteschlange.

02 · Denkmodell

Das mentale Modell

Der Main Thread sendet nur Daten plus Task-ID. Worker besitzen vorinstallierte Handler, führen einen Auftrag aus und antworten mit Resultat oder serialisiertem Fehler.

Im Repository

Führt vertrauenswürdige eigenständige Funktionen auf begrenzten Workern mit Timeout-Erholung aus. Funktionsquellcode kann keine Closures übernehmen und darf nie unvertrauenswürdig sein. Vorinstallierte Handler-Registries und Transferables sind mögliche Erweiterungen.

03 · Kontrollfluss

Was passiert in welcher Reihenfolge?

  1. Worker einmal starten und auf ready warten.
  2. Task-ID, registrierten Handlernamen und klonbare Payload in eine FIFO-Queue legen.
  3. Nur freie Worker belegen und genau eine laufende Task pro Worker zuordnen.
  4. Bei result oder error Promise beenden, Zuordnung löschen und nächsten Auftrag starten.
  5. Bei Exit laufende Task ablehnen und Worker je nach Shutdown-Status ersetzen.

04 · Bauteile

Die entscheidenden Verträge

WorkerPool.executeMethodPlant eine vertrauenswürdige eigenständige Funktion mit Argumenten ein.
WorkerPoolOptionsLegt Queue-Kapazität und Task-Timeout fest.
destroyLehnt offene Promises ab und wartet Worker-Ende ab.

05 · Selbst bauen

Selbst implementieren

Sende keine Funktionen. Der Worker lädt ein festes Registry-Modul und akzeptiert nur Taskname plus strukturklonbare Daten.

  1. Gemeinsames Message-Protokoll mit id, task, payload, result und error definieren.
  2. Worker-seitig erlaubte Handler in einer Registry auflösen.
  3. Manager-seitig pro ID resolve/reject halten und Worker erst nach Antwort freigeben.
Konzeptskizze · kann ausgelassene Helfer verwenden
// worker.ts
const handlers = {
  square: (value: number) => value * value,
};
parentPort?.on("message", async ({ id, task, payload }) => {
  try {
    const result = await handlers[task as keyof typeof handlers](payload);
    parentPort?.postMessage({ id, ok: true, result });
  } catch (error) {
    parentPort?.postMessage({ id, ok: false, error: String(error) });
  }
});

06 · Verifizieren

Was solltest du testen?

  • 0, false, leerer String und undefined beenden ihre Task ohne Hänger.
  • Ein Worker verarbeitet niemals zwei Aufgaben gleichzeitig und wird nach Fehler wieder frei.
  • Crash und destroy lehnen jede betroffene laufende oder wartende Task genau einmal ab.

Selbst ausprobieren

Formuliere die erste Prüfaussage als fehlschlagenden Test, implementiere die kleinste Korrektur und ergänze einen Grenzfall.

0, false, leerer String und undefined beenden ihre Task ohne Hänger.

Ansatz vergleichen

Dein Test sollte beim gezielten Verletzen der Invariante fehlschlagen und nach der Korrektur wieder bestehen. Halte den Aufbau deterministisch und prüfe ein beobachtbares Ergebnis.

07 · Grenzen

Kompromisse und Stolperfallen

  • Langlebige Worker amortisieren Startkosten, reservieren aber dauerhaft Speicher.
  • Eine Handler-Registry ist sicher und serialisierbar, aber weniger dynamisch als Funktionsübergabe.
  • Große Payloads kosten Structured-Clone-Zeit; Transferables können Kopien vermeiden.
Wichtig

Funktionsquellcode kann keine Closures übernehmen und darf nie unvertrauenswürdig sein. Vorinstallierte Handler-Registries und Transferables sind mögliche Erweiterungen.

08 · Weiterdenken

Quellcode und verwandte Ideen

yarn tsx packages/worker-pool/examples/basic.ts