4

I am reading the Akka (Java lib) docs and need clarification on some of their own proclaimed Akka/Actor Best Practices.

Actors should not block (i.e. passively wait while occupying a Thread) on some external entity...The blocking operations should be done in some special-cased thread which sends messages to the actors which shall act on them.

So what does a code example of this look like in Akka/Java? If an Actor isn't an appriote place to put code that has to block, then what does satisfy the definition of "some special-cased thread"?

Do not pass mutable objects between actors. In order to ensure that, prefer immutable messages.

I'm familiar with how to make immutable classes (no public setters, no public fields, make the class final, etc.). But does Akka have its own definition of an "immutable class", and if so, what is it?

Top-level actors are the innermost part of your Error Kernel...

I don't even know what this means! I understand what they mean by "top-level" actors (highest in the actor/manager/supervisor hierarchy), but what's an "Error Kernel", and how does it relate to actors?

smeeb
  • 27,777
  • 57
  • 250
  • 447
  • 2
    “Immutable” means that all fields are `final`, there is no modification happening externally or internally. Only then is the object safe to be shared among multiple threads without (costly) synchronization. – Roland Kuhn Apr 14 '15 at 06:30

2 Answers2

3

I am able to answer only the first question (and in future, please place only one question in a post). Consider, for example, a database connection, which is inherently blocking. In order to allow actors to connect to a database, programmer should create a dedicated thread (or a thread pool) with a queue of database requests. A request contains a database statement and a reference to the actor which is to receive the result. The dedicated thread reads requests in a loop, accesses the database, sends the result to the referenced actor etc. The request queue is blocking - when there are no requests, the connection thread is blocked in the queue.take() operation.

So the access to a database is split in two actors - one places a request to the queue, and the other handles the result.

UPDATE: Java code sketch (I am not strong in Scala).

class Request {
  String query;
  ActorRef handler;
}

class DatabaseConnector implements Runnable {
  LinkedBlockingQueue<Request> queue=new LinkedBlockingQueue<Request>();
  Thread  t = new Thread(this);
  {t.start();}

  public void sendRequest(Request r) {
     queue.put(r);
  }

  public void run() {
    for (;;) {
      Request r=queue.take();
      ResultSet res=doBlockingCallToJdbc(r.query);
      r.handler.sendOneWay(res);
    }
}
Alexei Kaigorodov
  • 13,189
  • 1
  • 21
  • 38
  • Thanks @Alexei K. (+1) - that is an excellent use case but I'm still struggling to see "the forest through the trees". Any chance you could update your answer with some pseudo-code, showing the `DatabaseRequest` object, the queue and anything else to make it complete? Don't worry about JDBC stuff, I think a `doBlockingDatabaseCall()` method would be sufficient to help me understand this 100%. Thanks again! – smeeb Apr 13 '15 at 18:44
2

Here is the answer for your second question. Right from the Akka Doc:

If one actor carries very important data (i.e. its state shall not be lost if avoidable), this actor should source out any possibly dangerous sub-tasks to children it supervises and handle failures of these children as appropriate. Depending on the nature of the requests, it may be best to create a new child for each request, which simplifies state management for collecting the replies. This is known as the “Error Kernel Pattern” from Erlang.

So the phrase you talking about means that these actors are the "last line of defence" from errors in your supervision hierarchy, so they should be strong and powerful guys (commandos) instead of some weak workers. And the less commandos you have - the easier it would be managing them and avoid mess at the top-level. Precisely saying, the count of commando's should be near to the count of business protocols you have (moving to the superheroes - let's say one for IronMan, one for Hulk etc.)

This document also has a good explanation about how to manage blocking operations.

Speaking of which

If an Actor isn't an appriote place to put code that has to block then what does satisfy the definition of "some special-cased thread

Actor definetely doesn't, because Akka guarantees only sequentiality, but your message may be processed on any thread (it just picks-up a free thread from the pool), even for single actor. Blocking operations are not recommended there (at least in same thread-pool with normal) because they may lead to performance problems or even deadlocks. See explanation for Spray (it's based on Akka) for instance : Spray.io: When (not) to use non-blocking route handling?

You may think of it like akka requires to interact only with asynchronous API. You may consider Future for converting sync to async - just send response from your database as a message to the actor. Example for scala:

 receive = { //this is receiving method onReceive
     case query: Query => //query is message safely casted to Query 
          Future { //this construction marks a peace of code (handler) which will be passed to the future 
            //this code will be executed in separate thread:
            doBlockingCallToJdbc(query)
          } pipeTo sender //means do `sender ! futureResult` after future's completion
     }
 }

Other approaches are described in the same document (Akka Doc)

dk14
  • 22,206
  • 4
  • 51
  • 88
  • Futures may not be the best solution. See the akka [docs](http://doc.akka.io/docs/akka/2.4/general/actor-systems.html#Blocking_Needs_Careful_Management) for all the ways you can handle the blocking. – Oleksandr.Bezhan Jan 21 '17 at 13:31
  • @Oleksndr.Bezhan that link is already included in my answer, Sasha. If you implying the sender-reference or failure reporting problem - it's solved with pipeTo. Simple request-response doesn't require a state, so no async mutation here. Pool management can be done by managing pool directly or importing a pool from akka-config. The first link I mentioned (and partially quoted) and you repeated (why?) is pointing to all possible solutions including Future. – dk14 Jan 21 '17 at 16:32