My Adventures in Coding

November 22, 2011

Scala – Handling failure in Actors with Akka Supervisors

Filed under: Actors,Scala — Brian @ 10:53 pm
Tags: , , ,

We have been using Akka Actors in all of our Scala projects for about a year now and have been very impressed. However, the problem with applications that have concurrently running Actors is that if an exception occurs and the Actor dies, the application may appear to still be up and running, but an Actor is now missing and know one knows this has happened. What we want in our application is to follow the “Let it fail” design philosophy by being able to restart an actor if one happens to fail. We want to guarantee that every Actor in the application is alive and well, even if an exception occurs. The easy way to obtain this type of fault tolerance in concurrent code is by using Actor Supervisors. The documentation on the Akka site is excellent, I highly recommend you read through it.

The basic idea is that the Supervisor is responsible for managing any Actors you ask it to manage. The supervisor starts the Actors it has been told to manage and in the event of an Actor crashing or being stopped, the supervisor will immediately restart the Actor for you.

The following is a simple example showing how to use a supervisor to start an Actor, that will automatically restart the Actor if an exception is thrown. To run the example you will need to download the Akka Actors library from the Akka Downloads page. Just download the zip, unzip it, and put the akka-actor-1.2.jar file from the lib/akka folder on your class path.

NOTE: This example was written using: Akka-Actor 1.2 and Scala 2.9.1

import akka.actor.Actor._
import akka.actor.{SupervisorFactory, Actor, Supervisor}
import akka.config.Supervision._

case object AreYouAlive
case object Kaboom

// Simple Actor that handles two types of messages: AreYouAlive and Kaboom
class SomeActor extends Actor {
  def receive = {
    case AreYouAlive => println("Yes I am alive!")
    case Kaboom => throw new RuntimeException("Kaboom!!!")
  }
}

object SomeApp extends App {
  val someActor = actorOf[SomeActor]

  // Setup the supervisorFactory with a config and a list of Actors to supervise
  val supervisorFactory = SupervisorFactory(
    SupervisorConfig(OneForOneStrategy(List(classOf[Exception]), 3, 1000),
    Supervise(someActor, Permanent) :: Nil)
  )
  // Instantiate and start the supervisor, this also starts all supervised Actors
  val supervisor = supervisorFactory.newInstance
  supervisor.start

  // Ping the Actor, yep it is working
  someActor ! AreYouAlive

  // Send the Kaboom message, oh no, the Actor will crash
  someActor ! Kaboom
  
  Thread.sleep(1000)

  // Wait, our Actor is alive and well again
  someActor ! AreYouAlive
  supervisor.shutdown()
}

Our Actor supervisor example will produce the following output:

Yes I am alive!
[ERROR]   [11/22/11 10:30 PM] [akka:event-driven:dispatcher:global-1] [LocalActorRef] Kaboom!!!
java.lang.RuntimeException: Kaboom!!!
	at SomeActor$$anonfun$receive$1.apply(ActorSupervisorExample.scala:12)
	at SomeActor$$anonfun$receive$1.apply(ActorSupervisorExample.scala:10)
	at akka.actor.Actor$class.apply(Actor.scala:545)
	at SomeActor.apply(ActorSupervisorExample.scala:9)
	at akka.actor.LocalActorRef.invoke(ActorRef.scala:905)
	at akka.dispatch.MessageInvocation.invoke(MessageHandling.scala:25)
	at akka.dispatch.ExecutableMailbox$class.processMailbox(ExecutorBasedEventDrivenDispatcher.scala:216)
	at akka.dispatch.ExecutorBasedEventDrivenDispatcher$$anon$4.processMailbox(ExecutorBasedEventDrivenDispatcher.scala:122)
	at akka.dispatch.ExecutableMailbox$class.run(ExecutorBasedEventDrivenDispatcher.scala:188)
	at akka.dispatch.ExecutorBasedEventDrivenDispatcher$$anon$4.run(ExecutorBasedEventDrivenDispatcher.scala:122)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:680)
	at akka.dispatch.MonitorableThread.run(ThreadPoolBuilder.scala:184)

Yes I am alive!

As you can see, even though the Actor threw an exception when it tried to handle the Kaboom message, the Actor was restarted and back to work right away. Great stuff!

I highly recommend reading the Fault Tolerance Through Supervisor Hierarchies documentation on the Akka website.

Create a free website or blog at WordPress.com.