Rerun Me

Akka Notes – Actor Supervision

Failures are more like a feature among distributed systems. And with Akka’s let it crash fault-tolerance model, you could achieve a clear separation between your business logic and your failure handling logic (supervision logic). All with very little effort. It’s pretty amazing. This is the topic of our discussion now.

ACTOR SUPERVISION

Imagine a method call stack and the top-most method in your stack throws an Exception. What could be done by the methods down the stack?

  1. The exception could be caught and handled in order to recover
  2. The exception could be caught, maybe logged and kept quiet.
  3. The methods down the stack could also choose to duck the exception completely (or maybe caught and rethrown)

Imagine if all the methods until the main method don’t handle the exception. In that case, the program exits after writing an essay for an exception to the console.

TESTIMONIAL: NETTIKASINO PARAS PALAUTUSPROSENTTI

“In online gaming and casino business, clear processes are especially important. That is where rerun.me & Akka Notes have proven to be extremely useful for us too.” – C. Von Max, Project Manager, Nettikasino Paras Palautusprosentti

Akka Notes – DeathWatch 

When we talked about Actor lifecycle, we saw that Actors could be stopped by various means (using ActorSystem.stop or ActorContext.stop or sending a PoisonPill – there’s also the Kill and the gracefulStop).

Whatever reason an Actor dies, there are cases when a few other actors in the system would like to know about it. Let’s take a trivial example of an Actor who talks to a database – let’s call it a RepositoryActor. For obvious reasons, there would be few other actors in the system who would be sending message to this RepositoryActor. These “interested” Actors would like to keep an eye on or watch this Actor if it goes down. Now, that in Actor terms is called DeathWatch. And the methods to watch and unwatch over this is intuitively ActorContext.watch and ActorContext.unwatch. If watched, the watchers would receive a Terminated message from the stopped Actor which they could comfortably add in to their receive function.

Unlike Supervision, where there is a strict enforcement of parent-child hierarchy, any Actor could watch any other Actor in the ActorSystem.

Akka Notes – Child Actors and ActorPath

Actors are completely hierarchical. Whatever Actors that you create HAS to be a child of some other Actor.

Let’s analyze that a bit :

PATH

Say, we create an ActorRef using ActorSystem.actorOf and try to print it’s path.

val actorSystem=ActorSystem("SupervisionActorSystem")  
val actorRef=actorSystem.actorOf(Props[BasicLifecycleLoggingTeacherActor])  
println (actorRef.path) // (prints) akka://SupervisionActorSystem/user/$a  

As you see, a path looks very similar to a file path in a file system.

  1. akka here is fixed because all these are addresses of Akka Actors – more like file:// or http:// prefix (nothing to do with protocol though).
  2. The SupervisionActorSystem is just the name of your ActorSystem that you created.
  3. We’ll talk about the user in the next section.
  4. The $a is the name of your Actor that the system generated for you. How would you like if your operating system generated random file names for your files? You’d obviously hate it because you would want to refer to that name in future. So, let’s give it a proper meaningful name to it :
val actorRef=actorSystem.actorOf(Props[BasicLifecycleLoggingTeacherActor], "teacherActor")  
println (actorRef.path)     // (prints) akka://SupervisionActorSystem/user/teacherActor

That’s it. Now, the path actually makes sense.

Akka Notes – Actor Lifecycle – Basic 

The basic Actor lifecycle is very much intuitive. You could actually compare the basic Actor lifecycle with a Java servlet lifecycle with one special difference.

  1. Just like any other regular class, we have a Constructor
  2. The preStart the method gets called back next. Here, you could initialize resources that you would like to clean-up in postStop
  3. The “servicing” or the message handling by the receive method occupies the major chunk of time and that happens in between.

Let’s look at a simple actor that prints the lifecycle.

Dumb Lifecycle Actor

package me.rerun.akkanotes.lifecycle

import akka.actor.{ActorLogging, Actor}  
import akka.event.LoggingReceive

class BasicLifecycleLoggingActor extends Actor with ActorLogging{

  log.info ("Inside BasicLifecycleLoggingActor Constructor")
  log.info (context.self.toString())
  override def preStart() ={
    log.info("Inside the preStart method of BasicLifecycleLoggingActor")
  }

  def receive = LoggingReceive{
    case "hello" => log.info ("hello")
  }

  override def postStop()={
    log.info ("Inside postStop method of BasicLifecycleLoggingActor")
  }

}

App

The LifecycleApp just initiates sends a message to the Actor and shuts down the ActorSystem.

import akka.actor.{ActorSystem, Props}

object LifecycleApp extends App{

  val actorSystem=ActorSystem("LifecycleActorSystem")
  val lifecycleActor=actorSystem.actorOf(Props[BasicLifecycleLoggingActor],"lifecycleActor")

  lifecycleActor!"hello"

  //wait for a couple of seconds before shutdown
  Thread.sleep(2000)
  actorSystem.shutdown()

 }

Output

Inside BasicLifecycleLoggingActor Constructor

Actor[akka://LifecycleActorSystem/user/lifecycleActor#-2018741361]

Inside the preStart method of BasicLifecycleLoggingActor

hello

Inside postStop method of BasicLifecycleLoggingActor  

What’s that special difference between Servlets and the basic Actor lifecycle?

That there is no difference between constructor and preStart in Actor lifecycle – more or less.

The reason why I printed the context.self in the constructor is this – unlike Servlets, Actors have access to the ActorContext even inside the constructor. The difference between the preStart and the constructor then becomes very subtle. We’ll revisit the difference while we talk about supervision but if you are curious – calling the preStart when the Actor restarts (in case of failure) could be controlled. With constructors, that isn’t possible.

Akka Notes – ActorSystem (Configuration and Scheduling)

As we saw from our previous posts, we could create an Actor using the actorOf method of the ActorSystem. There’s actually much more you could do with ActorSystem. We’ll touch upon just the Configuration and the Scheduling bit in this write-up

Let’s look at the subsets of methods available in the ActorSystem.

Remember the application.conf file we used for configuring our log level in the previous write-up? This configuration file is just like those .properties files in Java applications and much more. We’ll be soon seeing how we could use this configuration file to customize our dispatchers, mailboxes etc. (I am not even closely doing justice to the power of the typesafe config. Please go through some examples to really appreciate its awesomeness)

So, when we create the ActorSystem using the ActorSystem object’s apply method without specifying any configuration, it looks out for application.confapplication.json and application.properties in the root of the classpath and loads them automatically.

val system=ActorSystem("UniversityMessagingSystem")  

is the same as

val system=ActorSystem("UniversityMessagingSystem", ConfigFactory.load())  

To provide evidence to that argument, check out the apply method in ActorSystem.scala

  def apply(name: String, config: Option[Config] = None, classLoader: Option[ClassLoader] = None, defaultExecutionContext: Option[ExecutionContext] = None): ActorSystem = {
    val cl = classLoader.getOrElse(findClassLoader())
    val appConfig = config.getOrElse(ConfigFactory.load(cl))
    new ActorSystemImpl(name, appConfig, cl, defaultExecutionContext).start()
  }

Akka Notes – Actor Messaging – Request and Response

The last time when we saw Actor messaging, we saw how fire-n-forget messages are sent (Meaning, we just send a message to the Actor but don’t expect a response from the Actor).

Technically, we fire messages to Actors for their side-effects ALL THE TIME. It is by design. Other than not responding, the target Actor could ALSO do the following with that message –

  1. Send a response back to the sender (in our case, the TeacherActor would respond with a quote back to the StudentActor OR
  2. Forward a response back to some other Actor who might be the intended audience which in turn might respond/forward/have a side-effect. Routers and Supervisors are examples of those cases. (we’ll look at them very soon)

Akka Notes – Logging and Testing Actors

In the first two parts (one, two), we briefly talked about Actors and how messaging works. In this part, let’s look at fixing up Logging and Testing our TeacherActor.

RECAP

This is how our Actor from the previous part looked like :

class TeacherActor extends Actor {

  val quotes = List(
    "Moderation is for cowards",
    "Anything worth doing is worth overdoing",
    "The trouble is you think you have time",
    "You never gonna know if you never even try")

  def receive = {

    case QuoteRequest => {

      import util.Random

      //Get a random Quote from the list and construct a response
      val quoteResponse=QuoteResponse(quotes(Random.nextInt(quotes.size)))

      println (quoteResponse)

    }
  }
}

Akka Notes – Actor Messaging

In this first part of Actor Messaging, we’ll create the Teacher Actor and instead of the Student Actor, we’ll use a main program called StudentSimulatorApp.

Revisiting Student-Teacher in Detail

Let’s for now consider the message sent by the StudentSimulatorApp to the TeacherActor alone. When I say StudentSimulatorApp, I just mean a normal main program.

Akka Notes – Introducing Actors

I prefer not to talk about how Java concurrency API and their collections made it better and easier because I am sure if you are here, you probably needed more control over the sub-tasks or simply because you don’t like to write locks and synchronized blocks and would prefer a higher level of abstraction.In this series of Akka Notes, we would go through simple Akka examples to explore the various features that we have in the toolkit.

What are Actors?

Akka’s Actors follow the Actor Model (duh!).

Treat Actors like People. People who don’t talk to each other in person. They just talk through mails.

Let’s expand on that a bit.

1. Messaging

Consider two persons – A wise Teacher and Student. The Student sends a mail every morning to the Teacher and the wise Teacher sends a wise quote back.

Points to note :

  1. The student sends a mail. Once sent, the mail couldn’t be edited. Talk about natural immutability.
  2. The Teacher checks his mailbox when he wishes to do so.
  3. The Teacher also sends a mail back (immutable again).
  4. The student checks the mailbox at his own time.
  5. The student doesn’t wait for the reply. (no blocking)

That pretty much sums up the basic block of the Actor Model – passing messages.

QUICKSORTING – WAY AND DUAL PIVOT

So, what’s new about quicksort?Well, nothing except that I figured just now (after 2 damn years of release of Java 7) that the Quicksort implementation of the Arrays.sort has been replaced with a variant called Dual-Pivot QuickSort. This thread is not only awesome for this reason but also how humble Jon Bentley and Joshua Bloch really are.What did I do next?Just like everybody else, I wanted to implement it and do some benchmarking – against some 10 million integers (random and duplicate).Oddly enough, I found the following results :Random Data :

  • Quick Sort Basic : 1222 millis
  • Quick Sort 3 Way : 1295 millis (seriously !!)
  • Quick Sort Dual Pivot : 1066 millis

Duplicate Data :

  • Quick Sort Basic : 378 millis
  • Quick Sort 3 Way : 15 millis
  • Quick Sort Dual Pivot : 6 millis

Stupid Question 1 :

I am afraid that I am missing something in the implementation of 3-way partition. Across several runs against random inputs (of 10 million) numbers, I could see that the single pivot always performs better (although the difference is less than 100 milliseconds for 10 million numbers).

I understand that the whole purpose of making the 3-way Quicksort as the default Quicksort until now is that it does not give 0(n^2) performance on duplicate keys – which is very evident when I run it against duplicate input. But is it true that for the sake of handling duplicate data, a small penalty is taken by 3-way? Or is my implementation bad?

Stupid Question 2

My Dual Pivot implementation (link below) does not handle duplicates well. It takes a sweet forever (0(n^2)) to execute. Is there a good way to avoid this? Referring to the Arrays.sort implementation, I figured out that ascending sequence and duplicates are eliminated well before the actual sorting is done. So, as a dirty fix, if the pivots are equal I fast-forward the lowerIndex until it is different than pivot2. Is this a fair implementation?

    else if (pivot1==pivot2){
        while (pivot1==pivot2 && lowIndex<highIndex){
            lowIndex++;
            pivot1=input[lowIndex];
        }
    }

THE KNAPSACK PROBLEM

Given a Knapsack of a maximum capacity of W and N items each with its own value and weight, throw in items inside the Knapsack such that the final contents has the maximum value. Yikes !!!Here’s the general way the problem is explained – Consider a thief gets into a home to rob and he carries a knapsack. There are fixed number of items in the home – each with its own weight and value – Jewellery, with less weight and highest value vs tables, with less value but a lot heavy. To add fuel to the fire, the thief has an old knapsack which has limited capacity. Obviously, he can’t split the table into half or jewellery into 3/4ths. He either takes it or leaves it.Example :

    Knapsack Max weight     :       W = 10 (units)

    Total items             :       N = 4

    Values of items         :       v[] = {10, 40, 30, 50}

    Weight of items         :       w[] = {5, 4, 6, 3}

A cursory look at the example data tells us that the max value that we could accommodate with the limit of max weight of 10 is 50 + 40 = 90 with a weight of 7.

APPROACH

The way this is optimally solved is using dynamic programming – solving for smaller sets of knapsack problems and then expanding them for the bigger problem.

Let’s build an Item x Weight array called V (Value array)

    V[N][W] = 4 rows * 10 columns

Each of the values in this matrix represent a smaller Knapsack problem.

Base case 1 : Let’s take the case of 0th column. It just means that the knapsack has 0 capacity. What can you hold in them? Nothing. So, let’s fill them up all with 0s

Base case 2 : Let’s take the case of 0 row. It just means that there are no items in the house. What do you do hold in your knapsack if there are no items. Nothing again !!! All zeroes.

SOLUTION

1) Now, let’s start filling in the array row-wise. What does row 1 and column 1 mean? That given the first item (row), can you accommodate it in the knapsack with capacity 1 (column). Nope. The weight of the first item is 5. So, let’s fill in 0. In fact, we wouldn’t be able to fill in anything until we reach the column 5 (weight 5).

2) Once we reach column 5 (which represents weight 5) on the first row, it means that we could accommodate item 1. Let’s fill in 10 there (remember, this is a Value array)

3) Moving on, for weight 6 (column 6), can we accommodate anything else with the remaining weight of 1 (weight – weight of this item => 6 – 5). Hey, remember, we are on the first item. So, it is kind of intuitive that the rest of the row will just be the same value too since we are unable to add in any other item for that extra weight that we have got.

4) So, the next interesting thing happens when we reach the column 4 in third row. The current running weight is 4.

We should check for the following cases.

  1. Can we accommodate Item 2 – Yes, we can. Item 2’s weight is 4.
  2. Is the value for the current weight is higher without Item 2? – Check the previous row for the same weight. Nope. the previous row* has 0 in it, since we were not able able accommodate Item 1 in weight 4.
  3. Can we accommodate two items in the same weight so that we could maximize the value? – Nope. The remaining weight after deducting the Item2’s weight is 0.