Initializing typed actor behavior from an async (bootstrap) call?

Hi

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?

Thanks,
Age

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

Thank Johan!

I was hoping for an abstraction like this:

sealed trait Command

def bootstrap[Command](context: ActorContext[Command]): Future[Behavior[Command]] = ???

val behavior = Behaviors.setupAsync(bootstrap(_))

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
  }
}

Age

Using pipeToSelf looks good to me for this.

Here is another example using async interactions. Stash • Akka Documentation

Thanks @patriknw, that is indeed a pretty good example.