Why creating an actor inside an actor is dangerous

The akka documentation clearly states that creating an actor inside such an actor is dangerous:

class ActorA extends Actor { def receive = ??? } final class ActorB extends Actor { def receive = { case _ => val act = context.actorOf(Props(new ActorA)) }} 

I understand that the actor cast method accepts this link of the creating actor. but I could not understand (and could not find any example) why it is harmful and what problems can it cause?

+5
source share
3 answers

Tweak your example a bit

 class ActorA(str:String) extends Actor { def receive = ??? } final class ActorB extends Actor { def receive = { case _ => val act = context.actorOf(Props(new ActorA("hidden"))) }} 

Most common use cases for actors are for failures and control, not for the actor, and they need to be restarted; the actors system needs to know how to do this. When you use props (props (new ActorA)), you have hidden the value of the "hidden" parameter, processing it yourself.

Instead of doing this, instead you declare how to instantiate the actor, the actor system knows exactly what to do when the actor is recreated - i.e. create an instance of ActorA with the constructor argument "hidden".

Even with your example Actor without param

 context.actorOf(Props(new ActorA)) 

this method of creating instances of actors inside another actor is not recommended, because it encourages the closure of the covering area, which leads to non-serializable details and possibly race conditions (violation of actor encapsulation).

+3
source

I believe that we are misleading creation and announcement. Doc says that

Declaring one actor within another is very dangerous and breaks actor encapsulation. Never pass an actor's this reference into Props!

So, the problem is the declaration, not the creation! Let's take a look at Java:

 public class MyActor extends AbstractActor { @Override public Receive createReceive() { return ReceiveBuilder.create() .match(String.class, handleString()) .matchAny(x -> unhandled(x)) .build(); } private FI.UnitApply<String> handleString() { return message -> sender().tell("OK", getSelf()); } class MyOtherActor extends AbstractActor { @Override public Receive createReceive() { return ReceiveBuilder.create() .match(String.class, handleString()) .matchAny(x -> unhandled(x)) .build(); } private FI.UnitApply<String> handleString() { return message -> sender().tell("OK-Inner", getSelf()); } } } 

Now, if MyOtherActor was a normal class, we could only instantiate it from an instance of MyActor:

 MyActor actor = new MyActor(); MyActor.MyOtherActor otherActor = actor.new MyOtherActor(); 

This means that the constructor for MyOtherActor depends on the instance of MyActor!

Now, if the props should contain the "factory" of the actor. They need a factory method. If our MyOtherActor is declared as we are here, then our details will look like this (ish):

 MyActor actor = ??? // how did you even get a reference to the actor and not the actorRef in the first place! Props otherActorProps = Props.create(MyActor.MyOtherActor.class, () -> actor.new MyOtherActor()); 

And kick, here comes the kicker! Now your otherActorProps contains a link to actor , i.e. You have closed the variable state! If for any reason the actor "dies", your details will still refer to him, causing all sorts of oddities.

There is also a question about how you primarily get a link to actor , not actorRef

IMHO, what concerns the documentation, and not the fact of the "creation" (that is, the creation of an instance, spawning) of an actor in another: which is absolutely normal and this is a normal akka operation (that’s why you can do getContext().actorOf(..) , but also actorSystem.actorOf(...)

0
source

The warning is contained in the documentation, since it is easily accidentally closed from the actor’s creation state, including its this pointer (which you should never use in actor-based code).

In my experience, I usually saw the props method placed in the actor's companion object:

 object ActorA { def props() = Props(new ActorA) } 

Performing this method ensures that the returned props do not close the state of the actor.

 class ActorB extends Actor { def receive = { case _ => val actorB = context.actorOf(ActorA.props) ... } } 

This is not such a big opportunity for actors who do not accept constructor parameters, but as soon as the parameters come into play, you need to be careful when closing the internal state.

0
source

Source: https://habr.com/ru/post/1268829/


All Articles