We have been using typed actors for more that a year now. But the ActorSystem at top level was untyped, and we were creating typed actors from untyped system using spawn/spawnAnonymous adapter methods.
Now with Akka 2.5.22 being production ready we are thinking to migrate our untyped ActorSystem to typed. While trying to achieve this, in the process, we were having typed system and untyped system both existing in our code base for some time. Having both (typed or untyped) actor system at the same time gave us an exception:
object App1 extends App {
val typedSystem = ActorSystem(Behaviors.empty, "test")
val untypedSystem = typedSystem.toUntyped
untypedSystem.actorOf(Props.empty)
}
gives following error:
[error] Exception in thread "main" java.lang.UnsupportedOperationException: cannot create top-level actor from the outside on ActorSystem with custom user guardian
[error] at akka.actor.ActorSystemImpl.actorOf(ActorSystem.scala:786)
[error] at App1$.delayedEndpoint$App1$1(App1.scala:12)
[error] at App1$delayedInit$body.apply(App1.scala:6)
[error] at scala.Function0.apply$mcV$sp(Function0.scala:39)
[error] at scala.Function0.apply$mcV$sp$(Function0.scala:39)
[error] at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:17)
[error] at scala.App.$anonfun$main$1$adapted(App.scala:80)
[error] at scala.collection.immutable.List.foreach(List.scala:392)
[error] at scala.App.main(App.scala:80)
[error] at scala.App.main$(App.scala:78)
[error] at App1$.main(App1.scala:6)
[error] at App1.main(App1.scala)
While tracing back the exception, we got to know that untypedSystem.actorOf
checks if a custom guardian is wired or not, if it is then it throws the above mentioned exception and in case of untypedSystem being created from typed system will always have a guardian actor (in our case guardian actor is an actor with empty behaviour).
Hence, we concluded that typed system being converted to untyped system and actor created via actorOf
method is not an ideal approach. Maybe we should create untyped actor via ctx.actorOf
.
This popped up a question for us that if Extensions(CorordinatedShutdown, Http, Tcp, etc.) are taking untyped actor system and spawning actors internally, can we give typedSystem.toUntyped
to extensions (since we want to have typedSystem at the top level of our wiring)? The observation is that we can give typedSystem.toUntyped to extensions because they spawn actors via systemActorOf
instead of actorOf
and systemActorOf always create actors under systemGuardian so it does not fail with java.lang.UnsupportedOperationException: cannot create top-level actor from the outside on ActorSystem with custom user guardian
exception.
The next question is if a third party library creates extensions and if they are using actorOf
instead of systemActorOf
then having a typedSystem at the top and converting it to untyped to pass to extension will fail.
So, what should be the our approach of actorsystem at the top of our wiring :
- Do we keep typedSystem at the top and expect third party extensions to use
systemActorOf
- Or we keep untypedSystem at top and make it typed to use it everywhere for e.g. create
actors?