Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the default Scheduler in Akka, do I need to manually cancel events when the target actor stops?

When using the methods on the default Akka system scheduler (context().system().scheduler().schedule() from inside an actor), and one of the overloads accepting a destination actor, do I need to explicitly cancel using the returned Cancellable to free up resources when the destination actor stops?

I imagine the scheduler may be watch()ing the destination actor and automatically perform the cleanup but cannot find it explicitly state anywhere in the documentation.

like image 340
SoftMemes Avatar asked Apr 30 '13 12:04

SoftMemes


2 Answers

The variants of Scheduler.schedule which take an ActorRef will not watch that actor (which would have a rather high overhead relative to what a timer task is), hence you should always clean up the recurring timer from the actor’s postStop hook.

In the local case we check target.isTerminated, but that method always returns false for actor references which are not of the bog-standard local kind, hence you can rely on this feature only in specific cases and your code would then stop working correctly when you scale out your application. Another consideration is that the aforementioned check runs when trying to send the message, which can be a “soft leak” (i.e. deferred cleanup) in case of long schedules (where depending on the use-case 100ms can already be long).

like image 74
Roland Kuhn Avatar answered Oct 20 '22 07:10

Roland Kuhn


It looks like the task still runs, and the message more than likely ends up going to dead letter. You can see that behavior with the following code sample:

import akka.actor._
import scala.concurrent.duration._

object SchedTest {
  def main(args: Array[String]) {
    val sys = ActorSystem("test")
    val ref = sys.actorOf(Props[MyActor])

    val can = sys.scheduler.schedule(1 second, 1 second){
      println("executing")
      ref ! "foo"
    }(sys.dispatcher)

    Thread.sleep(10000)
    sys.stop(ref)
    Thread.sleep(5000)
    can.cancel
  }
}


class MyActor extends Actor{
  def receive = {
    case _ => 
      println("received message...")
  }
}

After 10 seconds, you will stop seeing the "received message..." string get printed, but you will continue to see the "executing" string get printed. Then after I manually kill the task, you stop seeing "executed" getting printed.

like image 36
cmbaxter Avatar answered Oct 20 '22 09:10

cmbaxter