I’m finally getting started with Akka Typed and I ran into this issue:
My main/root actor needs to bootstrap the system from persistent state
So from Behaviors.setup I would like to perform some async call to initialize everything and use that to produce the final Behavior
But there is no async version of Behaviors.setup.
Are there other ways of initializing a Behavior from a Future?
The only way I can see this work is to pipe the result of the future to self, wrapped in a message, and return a temporary Behavior that only handles that specific message. And then that temporary Behavior switches to the final full Behavior by handling that message.
Is that the right pattern for initializing an actor from an async call?
We do not have any public API for that, but I think it is relatively easy to achieve, and it will likely be something like what you describe. I think it is hard to abstract over in some generic form since the completion message needs to be part of the actor protocol.
We have a (somewhat special) deferred start used in the guardian actor in the Akka internals, stashing messages until some async operation completes and then switches to the “actual” behavior that you can see here: akka/GuardianStartupBehavior.scala at main · akka/akka · GitHub
But I agree that there are some difficult choices to be made about stashing behavior and what to do with failed bootstrap Futures that make it hard to fully abstract away.
So I ended up with this kind of pattern instead:
sealed trait Command
private case class Bootstrapped(...)
private def bootstrapped(...): Behavior[Command] =
Behaviors.receive { (context, command) =>
...
}
...
val behavior = Behaviors.setup[Command] { context =>
context.pipeToSelf(bootstrap(context)) {
case Success(bootstrapped: Bootstrapped) => bootstrapped
case Failure(exception) => ...
}
Behaviors.receiveMessage[Command] {
case bootstrapped: Bootstrapped => bootstrapped(...)
case _ => Behaviors.unhandled // or stash
}
}