Let’s assume an application implemented using Akka Typed has a persistent actor. This persistent actor as part of its operations creates transient (or non-persistent) children actors, each child has a unique ID and these IDs are part of the persisted state. The persistent actor also needs some way of communicating with its children, but we don’t want to persist children’s ActorRef
s as they aren’t really part of the state. On recovery the persistent actor should recreate its children based on the recovered state. It doesn’t sound like a very unusual use case, I’m trying to figure out what’s the cleanest way of implementing it. I could create the children actors inside the andThen
Effect
in my command handler which is meant for side effects, but then there’s no way to save the child’s ActorRef
from there. That seems to be a more general characteristic of the typed Persistence API - it’s very hard to have non-persistent state in persistent actors (which could be used for storing the transient children ActorRef
s in this case). One solution I came up with is having a sort of “proxy” actor for creating children, keeping a map of IDs and ActorRef
s, and forwarding messages based on IDs. The persistent actor holds a reference to that proxy actor and contacts it every time it needs to create a new child or send something to one of the existing children. I have mixed feelings about it though and would appreciate if somebody can point me to a better solution.
2 Answers
If you are not using snapshots then the persistence mechanism does not store the State
object, it stores the sequence of Event
s that led to that State
object. On recovery it simply re-plays those Event
s in the order in which they happened and your eventHandler
will return a modified State
object that reflects the effect of each event.
This means that the State
object can contain values that are not themselves persisted but are just set by the processing of certain Event
s. They are, in effect, cached values derived from the persistent values in the State
.
In your case the operation that causes the creation of a transient actor will be captured as an Event
on the actor. So you can create the transient actor in the eventHandler
and put the ActorRef
in the new State
object. When the actor is recovered it will replay that event and your actor will re-create the transient actor.
If you are using snapshots then I don't think there is a requirement that the snapshot object is the same type as your State
object, so you can snapshot the state without the ActorRef
s and re-create them when you get the SnapshotOffer
message.
It's a design goal of typed persistence that the State
be fully recoverable from the events (or from a snapshot and the events since that snapshot).
In general, the only way to have state that's non-persistent is to wrap the EventSourcedBehavior
in a Behaviors.setup
block which sets up the state. One option for this is some sort of mutable state (e.g. a var
or (likely exclusive or) mutable collection) in setup
, which the command/event/recovery handlers manipulate.
A much more immutable alternative is to define an immutable fixture in setup
, which includes a child actor which was spawned in setup
to manage non-persistent state. You can also put things like the entity ID or other things that are immutable for at least this incarnation of the entity into the fixture.