PoisonPill not killing the Akka actor system as intended - scala

I'm trying to get a child actor in my Scala 2.11 app (that uses Akka) to send a PoisonPill to the top-level actor (Master) and shut down the entire JVM process.
I uploaded this repo so you can reproduce what I'm seeing. Just clone it and run ./gradlew run.
But essentially we have the Driver:
object Driver extends App {
println("Starting upp the app...")
lazy val cortex = ActorSystem("cortex")
cortex.registerOnTermination {
System.exit(0)
}
val master = cortex.actorOf(Props[Master], name = "Master")
println("About to fire a StartUp message at Master...")
master ! StartUp
println("Fired! Actor system is spinning up...")
}
and then in the Child:
class Child extends Actor {
override def receive = {
case MakeItHappen =>
println("Child will make it happen!")
context.parent ! PoisonPill
}
}
When I run ./gradlew run I see the following output:
./gradlew run
Starting a Gradle Daemon (subsequent builds will be faster)
:compileJava NO-SOURCE
:compileScala UP-TO-DATE
:processResources NO-SOURCE
:classes UP-TO-DATE
:run
Starting upp the app...
About to fire a StartUp message at Master...
Fired! Actor system is spinning up...
Stage Director has received a command to start up the actor system!
Child will make it happen!
<==========---> 80% EXECUTING
> :run
However the JVM process never shuts down. I would have expected the PoisonPill to kill the Master (as well as Child), exit/shutdown the actor system, and then the System.exit(0) command (which I registered upon actor system termination) to kick in and exit the JVM process.
Can anyone tell what's going on here?

You're stopping the Master, but you're not shutting down the ActorSystem. Even though Master is a top-level actor (i.e., an actor created by system.actorOf), stopping it doesn't terminate the ActorSystem (note that Master itself has a parent, so it's not the "highest" actor in the actor hierarchy).
The normal way to terminate a ActorSystem is to call terminate. If you want the entire system to shut down if the Master stops, then you could override the Master's postStop hook and call context.system.terminate() there. Calling system.terminate() in an actor's lifecycle hook is risky, however. For example, if an exception is thrown in the actor, and the actor's supervision strategy stops it, you probably don't intend to shut down the entire system at that point. Such a design is not very resilient.
Check out this pattern for shutting down an actor system "at the right time."

Related

How to keep Scala Process Alive?

I type "sbt run", and compilation is ok, the server binds to the port, and then the process quietly terminates. How can I keep the process alive though? (I'm running an akka tcp server pulled straight from the akka docs -- should remain alive.)
Pseudocode:
object Main extends App {
val ac = ActorSystem();
val s = ac.actorOf(WebSocketManager.props());
}
Edit: The process stays alive after adding into build.sbt:
Compile / run / fork := true
Not sure what this does or if correct, but it works.

Using ForkJoinPool in Scala

In code:
val executor = new ForkJoinPool()
executor.execute(new Runnable{
def run = println("This task is run asynchronously")
})
Thread.sleep(10000)
This code prints: This task is run asynchronously
But if I remove Thread.sleep(10000), program doesn't print.
I then learnt that its so because sleep prevents daemon threads in ForkJoinPool from being terminated before they call run method on Runnable object.
So, few questions:
Does it mean threads started by ForkJoinPool are all daemon threads?Any why is it so?
How does sleep help here?
Answers:
Yes, because you are using the default thread factory and that is how it is configured. You can provide a custom thread factory to it if you wish, and you may configure the threads to be non-daemon.
Sleep helps because it prevents your program from exiting for long enough for the thread pool threads to find your task and execute it.

AKKA: Confusion about programmatic remote deployment

I am using akka remote deployment. I used logging to ensure if the actor has successfully deployed remotely. Here is my log info
[adaptiveCEP-akka.actor.default-dispatcher-18] [akka.tcp://adaptiveCEP#127.0.0.1:2555/remote/akka.tcp/adaptiveCEP#127.0.0.1:2554/user/disjunction/simple-2555-0.4631423946172286] hi, I am simple-2555-0.4631423946172286
[adaptiveCEP-akka.actor.default-dispatcher-18] [akka.tcp://adaptiveCEP#127.0.0.1:2555/remote/akka.tcp/adaptiveCEP#127.0.0.1:2554/user/disjunction/simple-2555-0.4631423946172286] hi, I am Actor[akka://adaptiveCEP/remote/akka.tcp/adaptiveCEP#127.0.0.1:2554/user/disjunction/simple-2555-0.4631423946172286#1386676347]
It appears as if actor simple-2555-0.4631423946172286#1386676347 is child actor of disjunction actor and both hosted on the same machine (no remote deployment of child). And the actor who is doing supervision is actor akka.tcp://adaptiveCEP#127.0.0.1:2555.
According to Top-Level Scopes for Actor Paths:
"/remote" is an artificial path below which all actors reside whose supervisors are remote actor references
Have I misunderstood something?
If required
val randomRouter = actorSystem.actorOf(Props[Master],
"disjunction")
Master.scala
val temp = context.actorOf(Props[SimpleClusterListener].withDeploy(Deploy(scope = RemoteScope(address))), "simple-" + port + "-" + Math.random())
temp ! "hi"
Reference
Create an Akka actor remotely without a new ActorSystem
No, your actor is not deployed locally, it's on the remote machine
akka.tcp://adaptiveCEP#127.0.0.1:2555/remote/akka.tcp/adaptiveCEP#127.0.0.1:2554/user/disjunction/simple-2555-0.4631423946172286] hi, I am simple-2555-0.4631423946172286
This log item shows that your actor is running on actor system "adaptiveCEP" on "127.0.0.1:2555" machine and is being supervised by "disjunction" actor in "adaptiveCEP#127.0.0.1:2554"

Enable tilde trigger with Akka and sbt

With sbt it is possible to do ~run to recompile and run the program when the source changes. However once an ActorSystem is created it does not work anymore.
When the system is shutdown it works, but I don't want to shutdown the system.
import akka.actor.ActorSystem
object Test {
def main(args: Array[String]) : Unit = {
val system = ActorSystem()
// if this line is removed, source code detection is disabled
system.shutdown()
}
}
Is there any workaround ?
You have to remember that SBTs ~run by itself is not hot-reloading, it is simply "once the process finishes, run it again please". Akka's thread-pools are non-daemonic, which means that until the ActorSystem is "alive" the program will not terminate, thus SBT's ~run won't trigger again.
Instead you might want to look into the sbt-revolver SBT plugin, which can do what you're looking for (including for Akka based apps).

Why does my actorFor fail when deployed to a Akka Microkernel JAR?

Have a somewhat simple project deployed to a JAR. I am starting up a supervisor actor that confirms it is booting up by sending out the following log message:
[akka://service-kernel/user/Tracker] Starting new Tracker
However, when I go to reference the actor via actorFor locally with an sbt run, it finds it no problem. In production, I use the same .actorFor("akka://service-kernel/user/Tracker") and it throws a NullPointerException. I can confirm via the logs that in production, the Tracker has sent out its confirmation that it booted up.
Are there any issues when using a Microkernel deployed to a JAR to make actor references?
Edit
I am suspecting that both the way I reference the system and the way Akka treats the start up class are related to the issue. Since I have specified a start up class called ServiceKernel, I am performing the reference as such: ServiceKernel.system.actorFor. Will provide an answer if confirmed.
Confirmed that it was related to the startup class handling the Microkernel.
The ServiceKernel mentioned above is used in the start script to boot up the Microkernel JAR: ./start com.package.ServiceKernel. In an sbt shell, this isn't needed so the alternative class I provided works well for referencing an Actor System.
However, in a Microkernel the ServiceKernel appears to be using a different Actor System altogether, so if you reference that system (like I did) then actorFor lookups will always fail. I solved the problem by passing the system down into the boot classes into the specific class where I was making the actorFor reference and it worked. Did it like this (pseudo-code):
class ServiceKernel extends Bootable {
val system = ActorSystem("service-kernel")
def startup = {
system.actorOf(Props(new Boot(isDev, system))) ! Start
}
}
And then passing it to an HttpApi class:
class Boot(val isDev: Boolean, system: ActorSystem) extends Actor with SprayCanHttpServerApp {
def receive = {
case Start =>
// setup HTTP server
val service = system.actorOf(Props(new HttpApi(system)), "tracker-http-api")
}
}