Remember entities
Este conteúdo não está disponível em sua língua ainda.
By default, sharded entities are lazy: an entity actor exists
only after it’s received its first message. Without
rememberEntities, a cluster cold-start or coordinator failover
means the active entity set is empty — entities respawn on demand
as messages arrive.
For many workloads this is fine. But sometimes you want entities to be eagerly recreated:
- They have scheduled background work that should run regardless of incoming messages.
- They’ve subscribed to something external and need to be alive to process it.
- Failover should restore the full working set instantly, not trickle through as users hit each entity.
rememberEntities: true is the toggle.
Enabling
Section titled “Enabling”const startShardingOptions = StartShardingOptions.create() .withTypeName('session') .withEntityActor(SessionActor) .withExtractEntityId((message) => message.userId) .withRememberEntities(true);sharding.start( startShardingOptions,);What it does:
- Each entity’s existence (its ID) is persisted to a remember-entities store.
- On cluster cold-start, or after a coordinator failover, the coordinator reads the set of known entity IDs and asks each shard region to re-spawn them eagerly.
The default store uses the system’s
Journal, so you get this for
free when persistence is configured. Override with
rememberEntitiesStore if you need a different backing store.
What’s persisted
Section titled “What’s persisted”Just the identities of active entities — not their state. Entity state is the entity’s own responsibility (PersistentActor journals state changes; remember-entities tracks which IDs to bring back).
Two-store split:
Journal (your events) RememberEntitiesStore (just IDs) persistenceId=cart-user-42 entityIds = ['cart-user-42', 'cart-user-43', ...] event 1 event 2 event 3This is intentional — the remember store has different access patterns (small, frequent writes when entities start/stop) and benefits from being separable.
What’s NOT persisted
Section titled “What’s NOT persisted”- The shard a given entity belonged to. On respawn, allocation runs again (per the allocation strategy).
- The entity’s state. If you want state to survive, the
entity must persist it itself via
PersistentActoror external storage.
Without persistence backend
Section titled “Without persistence backend”If you use rememberEntities: true but haven’t configured a
journal:
const startShardingOptions = StartShardingOptions.create() .withTypeName('...') .withEntityActor(...) .withExtractEntityId(...) .withRememberEntities(true) .withRememberEntitiesStore(null);sharding.start( startShardingOptions, // explicit opt-out of persistence);Passing null falls back to the v1 in-memory-only behavior — the
entity set is tracked in process memory and lost on coordinator
failover. Useful for dev / tests; pointless in production.
Without rememberEntitiesStore: null and without a configured
journal, the framework auto-instantiates a JournalRememberEntitiesStore
backed by the (in-memory by default) journal — which is fine for
testing but doesn’t survive restart. Make sure your journal
is durable in production.
Custom store
Section titled “Custom store”import type { RememberEntitiesStore, RememberEvent } from 'actor-ts';
class RedisRememberEntitiesStore implements RememberEntitiesStore { async append(typeName: string, event: RememberEvent): Promise<void> { /* RPUSH the event as JSON */ } async load(typeName: string): Promise<RememberEvent[]> { /* LRANGE 0 -1 → JSON.parse each */ return []; } async clear(typeName: string): Promise<void> { /* DEL the key */ }}
const shardingOptions = StartShardingOptions.create() // ... .withRememberEntities(true) .withRememberEntitiesStore(new RedisRememberEntitiesStore());sharding.start(shardingOptions);The interface is small. Useful if you want the entity registry on a different backing store than the rest of your persistence (e.g., Redis for low-latency entity-set lookups while the journal is on Cassandra).
rememberEntities: true adds two write operations per entity
lifetime:
- One write when the entity spawns (an
appendof astartedevent). - One write when the entity passivates / stops (an
appendof astoppedevent).
For high-churn entities (millions of short-lived sessions per hour), this is real overhead. Consider:
- Skip
rememberEntitiesfor short-lived workloads. - Use a fast store (in-memory Redis) instead of the default journal.
- Batch writes if you’re implementing a custom store.
For low-churn workloads (10K stable entities), the cost is negligible.
When to use it
Section titled “When to use it”| Scenario | Use rememberEntities? |
|---|---|
| Per-user sessions, short-lived, re-spawn on next request | No |
| Long-running coordinators, must be alive to do scheduled work | Yes |
| IoT device handlers, must subscribe to MQTT topics on startup | Yes |
| Per-tenant cache, expensive to rebuild | Yes |
| Per-order saga, must continue from where it left off after failover | Yes |
| Per-request transient actor, dies in seconds | No |
The rule: if “do I need this entity alive even without a recent message?” is yes, enable it.
Interaction with passivation
Section titled “Interaction with passivation”Passivation stops idle entities. With rememberEntities:
entity active → passivate (idle) → entity stops → store: append stopped?The framework’s behavior:
- Idle-timeout passivation (
passivationIdleMs) — appends astoppedevent. Next message respawns it; the store receives astartedevent again. maxEntitiesLRU passivation — same.Passivatefrom the entity itself — same.
For workloads where you want eager re-spawn even of recently-idle
entities, set passivationIdleMs: 0 so they stay around.
After an unexpected shard death
Section titled “After an unexpected shard death”A shard actor can die outside a rebalance — past its supervision budget,
say. The region keeps ownership (that is how the next message
re-creates the shard) and asks the coordinator to re-send what it
remembers for that shard, so the entities come back on their own without
waiting for traffic. Nothing is recorded as stopped in that case: the
entities were never meant to go.
Shard passivation does not interact with the registry at all — only an
empty shard is ever passivated, and its entities have already reported
their own stopped events by then.
Where to next
Section titled “Where to next”- Sharding overview — the bigger picture.
- Rebalance — what happens when remembered entities move.
- PersistentActor — for the entity’s state recovery (orthogonal to remember-entities).
- Journals — the default backing store.
