How to save data from actors on system shutdown - scala

For example I have following actors: Player and GameRoom.
GameRoom holds players with scores. When user left(terminates), we save player score in database:
class Player extends Actor {
...
}
object GameRoom {
case object Join
}
class GameRoom(database:ActorRef) extends Actor {
type Score = Int
var players: Map[ActorRef, Score] = Map.empty
def receive: Receive = {
case GameRoom.Join =>
context.watch(sender())
players = players + (sender() -> 100)
case Terminated(player) =>
players = players - player
database ! SavePlayerScore(...)
}
}
But what if I want to kill jvm process (SIGTERM)? In that case i have no way to save all users score to database on shutdown.
Any hints how to implement needed behaviour?

You can install a shutdown hook that will terminate your ActorSystem (using ActorSystem#terminate()) on application shutdown.
That will trigger an ordered termination of the Actors hierarchy, and in turn result in your GameRoom to receive the Terminated for the players.
Here is a small code snippet to install that shutdown hook:
Runtime.getRuntime.addShutdownHook(
new Thread("shutdown-hook") {
override def run() {
// This obviously needs to
try{
Await.ready(actorSystem.terminate(), Duration(2, TimeUnit.MINUTES))
}catch{
case _ : InterruptedException => // Termination was interrupted
case _ : Throwable => // Exception thrown by actor system termination
}}
})}
One important thing to notice here is: once the code in the Shutdown Hook is completed, the JVM will shutdown (killing all threads even if they are not done), so if you have any other cleanup to do, add it to the shutdown hook.
EDIT 1: The JVM will terminate even if the Await.ready threw an Exception. This means that, some of your state might have not been saved or something. You might want to handle those exception then and there because, again, once the run() method is complete, the JVM will die.

Related

Scala Akka Typed - send request inside behavior with ask

I'm kinda new to akka typed and I was trying to send a message which requires an answer within a given time.
I found the request-response pattern with ask which seemed interesting but is there a way to implement it inside of an already defined Behaviours.receive?
Here the idea is to call nextPlayerTurn each time a player answers or after a timeout
override def refereeTurn(): Behavior[Msg] = Behaviors.receive {
case (_, msg: GuessMsg) =>
if(currentPlayer.isDefined && currentPlayer.get == msg.getSender) {
controller ! msg
} else {
println("Player tried to guess after Timeout")
}
Behaviors.same
case (context, msg: ReceivedResponseMsg) =>
if(currentPlayer.isDefined && currentPlayer.get == msg.getSender)
nextPlayerTurn(context)
Behaviors.same
...
}
...
/**
* Tells to a player to start his turn and sets a timer that defines time in which a player has to make a guess.
* If such guess isn't made, sends that user an end turn message, fails the promise of his turn and allows next
* player to play his turn
*/
override def nextPlayerTurn(ctx: ActorContext[Msg]): Unit = {
implicit val timeout: Timeout = Timeout.timeout
currentPlayer = Option(turnManager.nextPlayer)
ctx.ask[Msg,Msg](currentPlayer.get, ref => YourTurnMsg(ref)) {
case Success(msg: GuessMsg) => println("\n SUCCESS"); msg
case Failure(_) => println(currentPlayer.get +" didn't guess in time"); TurnEnd(currentPlayer.get)
case _ => TurnEnd(currentPlayer.get)
}
}
In this case after the YourTurnMsg is sent the player is supposed to respond with a GuessMsg which stops the timer, this never happens due to the case matching inside refereeTurn Begaviour being executed instead of the Success (which instead always gives a Failure after the Timeout).
Did i get the wrong idea about the ask patter and should just make a new Behaviour with a timer?
If you want to use the ask pattern then the code that handles the result needs to send a message to the main actor rather than trying to do any processing directly. You can send a different message based on the result or just send the raw result and process it in the actor, but you must not do anything that depends on actor state in that code because it could be run on a different thread.
But ask is not cheap so in this case it seems better to just set a timer and see which message comes back first.

Can I safely create a Thread in an Akka Actor?

I have an Akka Actor that I want to send "control" messages to.
This Actor's core mission is to listen on a Kafka queue, which is a polling process inside a loop.
I've found that the following simply locks up the Actor and it won't receive the "stop" (or any other) message:
class Worker() extends Actor {
private var done = false
def receive = {
case "stop" =>
done = true
kafkaConsumer.close()
// other messages here
}
// Start digesting messages!
while (!done) {
kafkaConsumer.poll(100).iterator.map { cr: ConsumerRecord[Array[Byte], String] =>
// process the record
), null)
}
}
}
I could wrap the loop in a Thread started by the Actor, but is it ok/safe to start a Thread from inside an Actor? Is there a better way?
Basically you can but keep in mind that this actor will be blocking and a thumb of rule is to never block inside actors. If you still want to do this, make sure that this actor runs in a separate thread pool than the native one so you don't affect Actor System performances. One another way to do it would be to send messages to itself to poll new messages.
1) receive a order to poll a message from kafka
2) Hand over the
message to the relevant actor
3) Send a message to itself to order
to pull a new message
4) Hand it over...
Code wise :
case object PollMessage
class Worker() extends Actor {
private var done = false
def receive = {
case PollMessage ⇒ {
poll()
self ! PollMessage
}
case "stop" =>
done = true
kafkaConsumer.close()
// other messages here
}
// Start digesting messages!
def poll() = {
kafkaConsumer.poll(100).iterator.map { cr: ConsumerRecord[Array[Byte], String] =>
// process the record
), null)
}
}
}
I am not sure though that you will ever receive the stop message if you continuously block on the actor.
Adding #Louis F. answer; depending on the configuration of your actors they will either drop all messages that they receive if at the given moment they are busy or put them in a mailbox aka queue and the messages will be processed later (usually in FIFO manner). However, in this particular case you are flooding the actor with PollMessage and you have no guarantee that your message will not be dropped - which appears to happen in your case.

Play framework 2.0 long running tasks

My app has a long running task (anywhere from 5 minutes to 2 hours) that I can start through an admin panel.
I need a good way to know whether the task is currently running or not, so I can display the status on the admin panel and to prevent the task from being started twice.
Currently my code looks like this (simplified):
object TaskMonitor extends Controller {
var isRunning = false
// Displays the task status on the admin panel
def status = Action {
Ok.chunked(running &> Comet(callback = "parent.running"))
}
// Check task status every 100 ms
lazy val running: Enumerator[String] = {
Enumerator.generateM {
Promise.timeout(Some(isRunning.toString), 100 milliseconds)
}
}
// start the task, but only if it's not already running
def startTask = Action {
if (!isRunning) {
isRunning = true
val f = scala.concurrent.Future { Task.start }
f onComplete {
case _ => isRunning = false
}
}
Ok
}
}
Obviously this is has all kinds of issues, mainly the fact that I have unsynchronized mutable state (isRunning variable) in my controller.
What would be the correct way to go about what I want to achieve ?
You're right, you have unsynchronized mutable state. Is it really a problem? I mean this is your admin right? How many concurrent 'startTask' are you gonna send?
The recommended way to handle this in Play! is to use an Akka actor - you don't need any extra dependency, Play! includes Akka already.
Create an actor to hold your isRunning value; in startTask you can send a message to the actor to start the task (the actor will check if the task is not already running). To send the current status of the task you can query the actor for the value of isRunning.
This will give you a protected mutable state. Your choice to decide if it's really worth the trouble.

Scala Queue and NoSuchElementException

I am getting an infrequent NoSuchElementException error when operating over my Scala 2.9.2 Queue. I don't understand the exception becase the Queue has elements in it. I've tried switching over to a SynchronizedQueue, thinking it was a concurrency issue (my queue is written and read to from different threads) but that didn't solve it.
The reduced code looks like this:
val window = new scala.collection.mutable.Queue[Packet]
...
(thread 1)
window += packet
...
(thread 2)
window.dequeueAll(someFunction)
println(window.size)
window.foreach(println(_))
Which results in
32
java.util.NoSuchElementException
at scala.collection.mutable.LinkedListLike$class.head(LinkedListLike.scala:76)
at scala.collection.mutable.LinkedList.head(LinkedList.scala:78)
at scala.collection.mutable.MutableList.head(MutableList.scala:53)
at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
at scala.collection.mutable.MutableList.foreach(MutableList.scala:30)
The docs for LinkedListLike.head() say
Exceptions thrown
`NoSuchElementException`
if the linked list is empty.
but how can this exception be thrown if the Queue is not empty?
You should have window (mutable data structure) accessed from only a single thread. Other threads should send messages to that one.
There is Akka that allows relatively easy concurrent programming.
class MySource(windowHolderRef:ActorRef) {
def receive = {
case MyEvent(packet:Packet) =>
windowHolderRef ! packet
}
}
case object CheckMessages
class WindowHolder {
private val window = new scala.collection.mutable.Queue[Packet]
def receive = {
case packet:Packet =>
window += packet
case CheckMessages =>
window.dequeueAll(someFunction)
println(window.size)
window.foreach(println(_))
}
}
To check messages periodically you can schedule periodic messages.
// context.schedule(1 second, 1 second, CheckMessages)

Scala program exiting before the execution and completion of all Scala Actor messages being sent. How to stop this?

I am sending my Scala Actor its messages from a for loop. The scala actor is receiving the
messages and getting to the job of processing them. The actors are processing cpu and disk intensive tasks such as unzipping and storing files. I deduced that the Actor part is working fine by putting in a delay Thread.sleep(200) in my message passing code in the for loop.
for ( val e <- entries ) {
MyActor ! new MyJob(e)
Thread.sleep(100)
}
Now, my problem is that the program exits with a code 0 as soon as the for loop finishes execution. Thus preventing my Actors to finish there jobs. How do I get over this? This may be really a n00b question. Any help is highly appreciated!
Edit 1:
This solved my problem for now:
while(MyActor.getState != Actor.State.Terminated)
Thread.sleep(3000)
Is this the best I can do?
Assume you have one actor you're want to finish its work. To avoid sleep you can create a SyncVar and wait for it to be initialized in the main thread:
val sv = new SyncVar[Boolean]
// start the actor
actor {
// do something
sv.set(true)
}
sv.take
The main thread will wait until some value is assigned to sv, and then be woken up.
If there are multiple actors, then you can either have multiple SyncVars, or do something like this:
class Ref(var count: Int)
val numactors = 50
val cond = new Ref(numactors)
// start your actors
for (i <- 0 until 50) actor {
// do something
cond.synchronized {
cond.count -= 1
cond.notify()
}
}
cond.synchronized {
while (cond.count != 0) cond.wait
}