Mailbox-Sizing
Die Default-Mailbox eines Actors ist unbounded — die Queue eines Actors kann ohne Limit wachsen, wenn der Producer den Consumer überholt. Für die meisten Actors ist das in Ordnung. Wenn nicht, greifst du zu bounded Mailboxes.
Diese Seite ist der Entscheidungs-Guide für Produktions-Mailbox-Sizing.
Default-Verhalten
Abschnitt betitelt „Default-Verhalten“const ref = system.spawnAnonymous(Props.create(() => new Worker()));// ↑ unbounded FIFO-Mailbox; Mailbox kann bis zum OOM wachsenUnbounded Mailboxes sind schnell und nachsichtig — ein kurzer Burst wird absorbiert, der Actor arbeitet ihn irgendwann ab. Die meisten Actors sollten sie nutzen.
Die Falle: Ein anhaltendes Mismatch (Producer schneller als Consumer) lässt den Speicher monoton wachsen. Schließlich:
- Heap erschöpft → Prozess OOM-gekillt.
- Lange GC-Pausen → Cluster flappt.
- Kein Backpressure → der Producer weiß nicht, dass es ein Problem gibt.
Für diese Fälle: bounden.
Wann bounden
Abschnitt betitelt „Wann bounden“Drei Muster, in denen sich bounded Mailboxes lohnen:
1. Producer/Consumer-Mismatch vorab bekannt
Abschnitt betitelt „1. Producer/Consumer-Mismatch vorab bekannt“// Langsamer Consumer: schreibt 10/sec auf Disk; Producer pusht 1000/secconst slowWriter = system.spawn( Props.create(() => new SlowWriter()) .withMailbox(() => new BoundedMailbox({ capacity: 1_000, overflow: 'reject', })),);Bounde auf den Worst-Case-akzeptablen Puffer. reject propagiert
Backpressure zum Sender — er sieht MailboxFullError und passt
sich an (Retry, Drop, Alert).
2. Telemetrie-artige Actors (veraltete Daten sind falsch)
Abschnitt betitelt „2. Telemetrie-artige Actors (veraltete Daten sind falsch)“const telemetry = system.spawn( Props.create(() => new MetricsAggregator()) .withMailbox(() => new BoundedMailbox({ capacity: 5_000, overflow: 'drop-head', })),);Für Metriken, Sensorwerte, Status-Pings — frischer ist besser.
drop-head verwirft die älteste wartende Nachricht, wenn neue
ankommen, und hält die Queue mit aktuellen Daten gefüllt.
3. Kritische Actors nahe an Limits
Abschnitt betitelt „3. Kritische Actors nahe an Limits“const auth = system.spawn( Props.create(() => new AuthActor()) .withMailbox(() => new BoundedMailbox({ capacity: 10_000, overflow: 'drop-new', })),);drop-new verwirft eingehende Nachrichten, wenn voll — bewahrt
bereits eingereihte Arbeit. Richtig, wenn “die Queue, die ich habe,
ist die Arbeit, die mich interessiert” gilt — teilweiser Denial of
Service ist besser, als gar nichts zu verarbeiten.
Kapazität wählen
Abschnitt betitelt „Kapazität wählen“Drei Faktoren:
- Worst-Case-Burst-Größe — wieviele Nachrichten im Worst-Case-Fenster ankommen, bevor der Consumer drainen kann.
- Speicher pro Nachricht —
capacity × bytes_per_messagebegrenzt die Speicherkosten. - Latenz-Budget —
capacity / drain_ratebegrenzt die Worst-Case-Latenz, die eine Nachricht vor der Verarbeitung wartet.
Für einen Worker, der 100 msg/sec verarbeitet und Bursts bis zu 1000 msg in 1 Sekunde erwartet:
capacity = 1000 # Worst-Case-BurstWorst-Case-Latenz = 1000 / 100 = 10s # bei voller QueueWenn 10 Sekunden Queue okay sind, ist Kapazität 1000 fein. Wenn
nicht, Kapazität reduzieren oder akzeptieren, dass Producer
MailboxFullError sehen.
Die Metriken lesen
Abschnitt betitelt „Die Metriken lesen“Stock-Metriken (Stock-Metriken) exponieren die Mailbox-Tiefe:
actor_mailbox_size{class="Worker", path="..."}actor_mailbox_dropped_total{class="Worker", path="...", reason="drop-head"}Beobachte:
mailbox_size— hohe Werte relativ zur Kapazität deuten auf Druck hin.mailbox_dropped_total— ungleich Null mitdrop-head/drop-newist beabsichtigt; Spikes verdienen Untersuchung.MailboxFullError-Rate beim Sender — taucht meist als Supervisor-Restarts des sendenden Actors auf.
Die drei Overflow-Policies im Vergleich
Abschnitt betitelt „Die drei Overflow-Policies im Vergleich“| Policy | Wann |
|---|---|
reject (Default) | Backpressure schlägt zum Sender durch. Der Sender muss handeln. |
drop-head | Telemetrie / Metriken — Neueste gewinnt. |
drop-new | Kritische Arbeit — Eingereihtes behalten, Eingehendes droppen. |
Wähle nach der richtigen Antwort auf Overflow:
- “Sender soll retryen / alerten” →
reject. - “Veraltete Daten sind falsch” →
drop-head. - “Eingereihte Arbeit ist wertvoll” →
drop-new.
Es gibt kein “Bestes” — kontextabhängig.
Priority-Mailboxes
Abschnitt betitelt „Priority-Mailboxes“Für Actors mit gemischter Dringlichkeit:
import { PriorityMailbox } from 'actor-ts';
const worker = system.spawn( Props.create(() => new Worker()) .withMailbox(() => new PriorityMailbox<Msg>({ priorityFor: (m) => m.kind === 'urgent' ? 0 : 5, })),);Niedrigere Zahlen = höhere Priorität. System-Nachrichten übertrumpfen immer.
Nutze für:
- HTTP-Antworten (dringend) vs Batch-Jobs (aufschiebbar).
- Health-Pings vs Bulk-Metriken.
Siehe Mailboxes für die volle PriorityMailbox-Oberfläche.
Mailbox + Backpressure-Design
Abschnitt betitelt „Mailbox + Backpressure-Design“producer → reject Backpressure → Sender verlangsamt sichproducer → drop-head → Producer macht weiter; Reader sieht das Neuesteproducer → drop-new → Producer macht weiter; Reader verarbeitet das ÄltesteBounded Mailboxes sind eine Ebene in einer Backpressure-Story. Für Ende-zu-Ende-Backpressure (das vorgelagerte System wird langsamer) kombinierst du:
- Bounded Mailbox am Actor.
- Sender-Retry-Logik.
- Upstream-Rate-Limiting (HTTP 429, Broker-Pushback).
Die Mailbox erzwingt die lokale Grenze; der Rest ist dein Protokoll-Design.
Wohin als nächstes
Abschnitt betitelt „Wohin als nächstes“- Mailboxes — die konzeptuelle Referenz.
- Stock-Metriken — die Mailbox-Depth- + Drop-Metriken.
- Dispatcher-Tuning — der komplementäre Knopf.
- Backoff-Supervisor — für Stash-on-Fail mit bounded Puffer.