コンテンツにスキップ
日本語

PersistentFSM

このコンテンツはまだ日本語訳がありません。

Defined in: src/fsm/PersistentFSM.ts:218

Classic-style event-sourced actor. Subclasses override onCommand (which decides what to persist), onEvent (pure state update from the event), and optionally onRecoveryComplete. Commands are automatically stashed while persist(...) is pending, so user code can assume the state is caught up by the time its callback fires.

class AccountActor extends PersistentActor<Command, Event, State> { readonly persistenceId = ‘account-42’; initialState(): State { return { balance: 0 }; } onEvent(state: State, e: Event): State { if (e.kind === ‘deposited’) return { balance: state.balance + e.amount }; return state; } onCommand(state: State, command: Command): void { if (command.kind === ‘deposit’) { this.persist({ kind: ‘deposited’, amount: command.amount }, (s) => { this.sender.forEach(replyTo => replyTo.tell({ ok: s.balance })); }); } } }

Command extends object

Event

SName extends string

Data

new PersistentFSM<Command, Event, SName, Data>(): PersistentFSM<Command, Event, SName, Data>

PersistentFSM<Command, Event, SName, Data>

PersistentActor.constructor

abstract readonly persistenceId: string

Defined in: src/persistence/PersistentActor.ts:61

PersistentActor.persistenceId


abstract transitions: FsmTransitionMap<SName, Command, Event, Data>

Defined in: src/fsm/PersistentFSM.ts:244

Transition table. Implementations typically declare it as a class field so the type-narrowing in FsmTransitionMap works at the call site (transitions[state][commandKind]).

abstract applyEvent(state, data, event): FsmStateData<SName, Data>

Defined in: src/fsm/PersistentFSM.ts:235

Pure event-application — updates both state name and data. Runs at persist-time (forward) AND at recovery-time (replay), so it MUST be deterministic and free of side effects.

SName

Data

Event

FsmStateData<SName, Data>


compression(): CompressionConfig | undefined

Defined in: src/persistence/PersistentActor.ts:134

Per-actor compression — overrides the plugin default for THIS actor’s snapshots. Stores that don’t compress (in-memory, SQLite, Cassandra) ignore the value. Returning undefined (the default) defers to the plugin’s resolver / configured default.

CompressionConfig | undefined

PersistentActor.compression


displayName(): string

Defined in: src/Actor.ts:192

Human-readable name for this actor in log lines and in the DevTools actor tree (#891). Defaults to the full path — which is already the log source, so an actor that doesn’t override this logs exactly as it did before.

override displayName(): string { return `User(${this.entityId})`; }

Purely cosmetic. The path stays the identity everywhere that routes, correlates or aggregates — metric labels, tracing attributes, dead letters, ActorRef.toString(), every wire identifier — so a display name is free to be ambiguous, unstable, or shared between actors.

Resolved on every record, not captured once. Two consequences: keep it cheap and side-effect free, and expect it to be called before preStart (hence the optional chain — the context is attached after construction). In exchange a name may be derived from state, and it updates when that state does. Throwing, or returning anything but a non-empty string, falls back to the path and warns once: a naming hook must not be able to take a log line down with it.

ActorOptions.withDisplayName(...) outranks this, for the same reason withSupervisorStrategy(...) outranks supervisorStrategy — the spawn site is the more specific statement. It has to: every Behaviors actor is a TypedActor that inherits this default, so a method that won would silently swallow the spawn-site value for exactly the actors that have no subclass to override. For a name that only becomes known at runtime, this.context.setDisplayName(...) outranks both.

string

PersistentActor.displayName


encryption(): EncryptionConfig | undefined

Defined in: src/persistence/PersistentActor.ts:142

Per-actor encryption — overrides the plugin default for THIS actor’s snapshots. Honoured by stores that encrypt at rest (object-storage); other stores ignore it. Used on both the write path (encrypt) and the read path (derive subkey from master to decrypt).

EncryptionConfig | undefined

PersistentActor.encryption


eventAdapter(): EventAdapter<Event, Event> | undefined

Defined in: src/persistence/PersistentActor.ts:118

Optional event adapter for schema evolution. When defined, every persisted event is wrapped into a { _v, _t, _e } envelope on the write path and unwrapped (with up-casting through the adapter) on the read path. Recovery is strict when an adapter is set: a raw, non-envelope event in the journal will throw MigrationError. See src/persistence/migration/.

EventAdapter<Event, Event> | undefined

PersistentActor.eventAdapter


abstract initialData(): Data

Defined in: src/fsm/PersistentFSM.ts:228

Starting data when no events have been replayed.

Data


abstract initialFsmState(): SName

Defined in: src/fsm/PersistentFSM.ts:225

Starting state name when no events have been replayed.

SName


initialState(): FsmStateData<SName, Data>

Defined in: src/fsm/PersistentFSM.ts:296

Default initial state when no snapshot and no events exist.

FsmStateData<SName, Data>

PersistentActor.initialState


onCommand(curr, command): Promise<void>

Defined in: src/fsm/PersistentFSM.ts:331

Handle an incoming command — typically calls persist(event, afterPersist).

FsmStateData<SName, Data>

Command

Promise<void>

PersistentActor.onCommand


onEvent(curr, event): FsmStateData<SName, Data>

Defined in: src/fsm/PersistentFSM.ts:300

Pure state-update function — MUST be deterministic, and is deliberately synchronous where onCommand is async.

A command decides and therefore does I/O (persist writes to the journal); an event is already a fact, and folding a fact into state is arithmetic. Anything you would want to await here — a read, a notification — is precisely what must NOT run again on recovery. Put it in the persist callback or onRecoveryComplete, neither of which replay.

Read state from the parameter, never this.state: during replay this runs detached inside replayState, before this.state has been assigned, and the DevTools time-travel panel borrows it as a free fold. A handler that reads this.state works on the persist path and fails only after a restart.

FsmStateData<SName, Data>

Event

FsmStateData<SName, Data>

PersistentActor.onEvent


onReceive(message): Promise<void>

Defined in: src/fsm/PersistentFSM.ts:322

Intercept the internal __fsm_state_timeout__ self-tell that the armed timer routes through the mailbox. Real user commands delegate straight to super.onReceive (which handles recovery stash + persist gating + dispatch to onCommand).

Command

Promise<void>

PersistentActor.onReceive


onRecoveryComplete(_state): void | Promise<void>

Defined in: src/fsm/PersistentFSM.ts:304

Called once recovery finishes, with the final replayed state.

FsmStateData<SName, Data>

void | Promise<void>

PersistentActor.onRecoveryComplete


onRecoveryFailure(reason): void

Defined in: src/persistence/PersistentActor.ts:102

Called when recovery itself throws.

A notification, not a decision — recovery failure is terminal either way. The default rethrows, so the failure reaches supervision as an ActorInitializationError. An override that returns normally takes the failure as handled, and the actor is then stopped: state was never assigned and lastSequenceNr is unknown, so there is no state in which it could answer a command. Pending commands go to dead letters rather than disappearing.

Error

void

PersistentActor.onRecoveryFailure


postRestart(_reason): void | Promise<void>

Defined in: src/Actor.ts:152

Called on the fresh instance after a restart. Default: call preStart().

Error

void | Promise<void>

PersistentActor.postRestart


postStop(): Promise<void>

Defined in: src/fsm/PersistentFSM.ts:312

Called after the actor has been terminated. Children are already stopped.

Promise<void>

PersistentActor.postStop


preRestart(_reason, _message?): void | Promise<void>

Defined in: src/Actor.ts:119

Called before a restart, on the instance about to be thrown away. The default calls postStop() and nothing else.

Override to release what the instance holds outside itself — a file handle, an open socket, a broker connection — or to do something other than drop the message that failed.

Stopping this actor’s children is not done here: the framework tears them down after this hook returns and waits for them before building the replacement, because postRestart re-runs preStart and a named child needs its name back. To keep the children instead, see Actor.stopChildrenOnRestart.

Error

Command

void | Promise<void>

PersistentActor.preRestart


preStart(): Promise<void>

Defined in: src/persistence/PersistentActor.ts:166

Called after construction and before the first message is processed.

Promise<void>

PersistentActor.preStart


snapshotAdapter(): SnapshotAdapter<FsmStateData<SName, Data>, FsmStateData<SName, Data>> | undefined

Defined in: src/persistence/PersistentActor.ts:126

Optional snapshot adapter — same semantics as eventAdapter, but applied to the state blob persisted by the snapshot store. When a snapshot adapter is set and a stored snapshot is not an envelope, recovery throws.

SnapshotAdapter<FsmStateData<SName, Data>, FsmStateData<SName, Data>> | undefined

PersistentActor.snapshotAdapter


snapshotPolicy(): SnapshotPolicy<FsmStateData<SName, Data>, Event>

Defined in: src/persistence/PersistentActor.ts:105

Snapshot policy — return true to snapshot the current state.

SnapshotPolicy<FsmStateData<SName, Data>, Event>

PersistentActor.snapshotPolicy


stopChildrenOnRestart(): boolean

Defined in: src/Actor.ts:149

Whether a restart tears this actor’s children down before rebuilding it. Default: true.

A restart replaces the Actor instance while the cell — and therefore the child map — survives. Keeping the children was the old behaviour and it made an ordinary pattern impossible: postRestart re-runs preStart, so an actor that spawns a named child there hit Child name … is not unique on its first restart and never recovered (#634).

Override to false when the children are expensive to rebuild, hold state the parent cannot restore, or are supervised independently — a connection pool, say. They then outlive the restart exactly as before, and it is on you to make preStart idempotent — by adopting the survivor from the cell, this.child = this.context.child('name').toNullable() ?? this.context.spawn(Child, 'name'), or with context.spawnAnonymous. An instance field cannot do it: preStart runs on a fresh instance after every restart, so this.child ??= … is always unset and re-spawns into the name the surviving child still holds, which fails the spawn and restarts the actor again.

This is a separate hook rather than a preRestart override because the teardown has to be awaited: the new instance cannot be built until the old children are actually gone, and preRestart has no way to tell the cell that it started something worth waiting for.

boolean

PersistentActor.stopChildrenOnRestart


supervisorStrategy(): SupervisorStrategy

Defined in: src/Actor.ts:160

Supervisor strategy for this actor’s children. Defaults to restart, up to 10 times per minute, then stop.

SupervisorStrategy

PersistentActor.supervisorStrategy


tagsFor(_event): readonly string[] | undefined

Defined in: src/persistence/PersistentActor.ts:108

Optional tags attached to every persisted event (for Persistence Query).

Event

readonly string[] | undefined

PersistentActor.tagsFor