Remember Entities
Sharded Entities sind standardmäßig lazy: ein Entity-Actor
existiert erst, nachdem er seine erste Nachricht empfangen hat.
Ohne rememberEntities bedeutet ein Cluster-Kaltstart oder
Koordinator-Failover, dass die aktive Entity-Menge leer ist —
Entities werden bei Bedarf neu gespawnt, sobald Nachrichten
ankommen.
Für viele Workloads ist das in Ordnung. Manchmal aber willst du, dass Entities eager neu erstellt werden:
- Sie haben geplante Hintergrundarbeit, die unabhängig von eingehenden Nachrichten laufen sollte.
- Sie haben etwas Externes abonniert und müssen am Leben sein, um es zu verarbeiten.
- Failover sollte das volle Arbeitsset sofort wiederherstellen, nicht durchsickern, sobald Nutzer auf jede Entity treffen.
rememberEntities: true ist der Schalter.
Aktivieren
Abschnitt betitelt „Aktivieren“const startShardingOptions = StartShardingOptions.create() .withTypeName('session') .withEntityActor(SessionActor) .withExtractEntityId((message) => message.userId) .withRememberEntities(true);sharding.start( startShardingOptions,);Was es tut:
- Die Existenz jeder Entity (ihre ID) wird in einem Remember-Entities-Store persistiert.
- Beim Cluster-Kaltstart oder nach einem Koordinator-Failover liest der Koordinator die Menge bekannter Entity-IDs und bittet jede Shard-Region, sie eager neu zu spawnen.
Der Default-Store nutzt das Journal des
Systems, sodass du das umsonst bekommst, sobald Persistenz
konfiguriert ist. Überschreibe mit rememberEntitiesStore, wenn du
einen anderen Backing-Store brauchst.
Was persistiert wird
Abschnitt betitelt „Was persistiert wird“Nur die Identitäten aktiver Entities — nicht ihr Zustand. Der Entity-Zustand ist die Verantwortung der Entity selbst (PersistentActor journalisiert Zustandsänderungen; Remember-Entities verfolgt, welche IDs zurückgebracht werden sollen).
Zwei-Speicher-Aufteilung:
Journal (deine Events) RememberEntitiesStore (nur IDs) persistenceId=cart-user-42 entityIds = ['cart-user-42', 'cart-user-43', ...] Event 1 Event 2 Event 3Das ist absichtlich — der Remember-Store hat andere Zugriffsmuster (kleine, häufige Writes, wenn Entities starten/stoppen) und profitiert davon, separierbar zu sein.
Was NICHT persistiert wird
Abschnitt betitelt „Was NICHT persistiert wird“- Der Shard, zu dem eine gegebene Entity gehört hat. Bei Respawn läuft die Allokation erneut (per Allokationsstrategie).
- Der Zustand der Entity. Wenn du willst, dass Zustand
überlebt, muss die Entity ihn selbst persistieren — über
PersistentActoroder externen Storage.
Ohne Persistenz-Backend
Abschnitt betitelt „Ohne Persistenz-Backend“Wenn du rememberEntities: true verwendest, aber kein Journal
konfiguriert hast:
const startShardingOptions = StartShardingOptions.create() .withTypeName('...') .withEntityActor(...) .withExtractEntityId(...) .withRememberEntities(true) .withRememberEntitiesStore(null);sharding.start( startShardingOptions, // explizites Opt-Out aus Persistenz);null zu übergeben fällt zurück auf das v1-In-Memory-Only-Verhalten —
die Entity-Menge wird im Prozessspeicher verfolgt und bei
Koordinator-Failover verloren. Nützlich für Dev / Tests; sinnlos
in Produktion.
Ohne rememberEntitiesStore: null und ohne konfiguriertes Journal
instanziiert das Framework automatisch einen
JournalRememberEntitiesStore mit dem (standardmäßig In-Memory)
Journal — was zum Testen in Ordnung ist, aber einen Neustart
nicht überlebt. Stelle sicher, dass dein Journal in Produktion
dauerhaft ist.
Eigener Store
Abschnitt betitelt „Eigener Store“import type { RememberEntitiesStore, RememberEvent } from 'actor-ts';
class RedisRememberEntitiesStore implements RememberEntitiesStore { async append(typeName: string, event: RememberEvent): Promise<void> { /* RPUSH das Event als JSON */ } async load(typeName: string): Promise<RememberEvent[]> { /* LRANGE 0 -1 → jeweils JSON.parse */ return []; } async clear(typeName: string): Promise<void> { /* DEL den Key */ }}
const shardingOptions = StartShardingOptions.create() // ... .withRememberEntities(true) .withRememberEntitiesStore(new RedisRememberEntitiesStore());sharding.start(shardingOptions);Die Schnittstelle ist klein. Nützlich, wenn du die Entity-Registry auf einem anderen Backing-Store als den Rest deiner Persistenz willst (z. B. Redis für latenzarme Entity-Set-Lookups, während das Journal auf Cassandra liegt).
rememberEntities: true fügt zwei Write-Operationen pro
Entity-Lebenszyklus hinzu:
- Ein Write, wenn die Entity spawnt (ein
appendeinesstarted-Events). - Ein Write, wenn die Entity passiviert / stoppt (ein
appendeinesstopped-Events).
Für High-Churn-Entities (Millionen kurzlebiger Sessions pro Stunde) ist das echter Overhead. Erwäge:
rememberEntitiesfür kurzlebige Workloads überspringen.- Einen schnellen Store (In-Memory-Redis) statt des Default-Journals nutzen.
- Writes batchen, wenn du einen eigenen Store implementierst.
Für Low-Churn-Workloads (10K stabile Entities) sind die Kosten vernachlässigbar.
Wann verwenden
Abschnitt betitelt „Wann verwenden“| Szenario | rememberEntities nutzen? |
|---|---|
| Per-User-Sessions, kurzlebig, beim nächsten Request neu spawnen | Nein |
| Lange laufende Koordinatoren, müssen leben, um geplante Arbeit zu tun | Ja |
| IoT-Device-Handler, müssen beim Start MQTT-Topics abonnieren | Ja |
| Per-Tenant-Cache, teuer zu rebauen | Ja |
| Per-Order-Saga, muss nach Failover dort weiter, wo sie aufgehört hat | Ja |
| Per-Request-temporärer-Actor, stirbt in Sekunden | Nein |
Die Regel: wenn “brauche ich diese Entity am Leben auch ohne kürzliche Nachricht?” ein Ja ist, aktivieren.
Interaktion mit Passivierung
Abschnitt betitelt „Interaktion mit Passivierung“Passivierung stoppt idle Entities. Mit rememberEntities:
Entity aktiv → passiviere (idle) → Entity stoppt → Store: append stopped?Verhalten des Frameworks:
- Idle-Timeout-Passivierung (
passivationIdleMs) — hängt einstopped-Event an. Nächste Nachricht spawnt sie neu; der Store empfängt erneut einstarted-Event. maxEntities-LRU-Passivierung — dasselbe.Passivatevon der Entity selbst — dasselbe.
Für Workloads, in denen du eager Re-Spawn auch von kürzlich
inaktiven Entities willst, setze passivationIdleMs: 0, damit
sie bleiben.
Nach einem unerwarteten Shard-Tod
Abschnitt betitelt „Nach einem unerwarteten Shard-Tod“Ein Shard-Actor kann außerhalb eines Rebalance sterben — etwa jenseits
seines Supervision-Budgets. Die Region behält die Ownership (so
erstellt die nächste Nachricht den Shard neu) und fragt beim
Koordinator nach, was er sich für diesen Shard merkt, sodass die
Entities von allein zurückkommen, ohne auf Verkehr zu warten. Es wird
dabei nichts als stopped verzeichnet: die Entities sollten nie gehen.
Shard-Passivierung berührt die Registry gar nicht — passiviert wird
immer nur ein leerer Shard, und seine Entities haben ihre eigenen
stopped-Events zu diesem Zeitpunkt längst gemeldet.
Wohin als Nächstes
Abschnitt betitelt „Wohin als Nächstes“- Sharding-Überblick — das größere Bild.
- Rebalance — was passiert, wenn gemerkte Entities wandern.
- PersistentActor — für die Zustands-Recovery der Entity (orthogonal zu Remember-Entities).
- Journals — der Default-Backing-Store.
