Take the 2-minute tour ×

I'm trying to build a high-performance distributed system with Akka and Scala.

If a message requesting an expensive (and side-effect-free) computation arrives, and the exact same computation has already been requested before, I want to avoid computing the result again. If the computation requested previously has already completed and the result is available, I can cache it and re-use it.

However, the time window in which duplicate computation can be requested may be arbitrarily small. e.g. I could get a thousand or a million messages requesting the same expensive computation at the same instant for all practical purposes.

There is a commercial product called Gigaspaces that supposedly handles this situation.

However there seems to be no framework support for dealing with duplicate work requests in Akka at the moment. Given that the Akka framework already has access to all the messages being routed through the framework, it seems that a framework solution could make a lot of sense here.

Here is what I am proposing for the Akka framework to do: 1. Create a trait to indicate a type of messages (say, "ExpensiveComputation" or something similar) that are to be subject to the following caching approach. 2. Smartly (hashing etc.) identify identical messages received by (the same or different) actors within a user-configurable time window. Other options: select a maximum buffer size of memory to be used for this purpose, subject to (say LRU) replacement etc. Akka can also choose to cache only the results of messages that were expensive to process; the messages that took very little time to process can be re-processed again if needed; no need to waste precious buffer space caching them and their results. 3. When identical messages (received within that time window, possibly "at the same time instant") are identified, avoid unnecessary duplicate computations. The framework would do this automatically, and essentially, the duplicate messages would never get received by a new actor for processing; they would silently vanish and the result from processing it once (whether that computation was already done in the past, or ongoing right then) would get sent to all appropriate recipients (immediately if already available, and upon completion of the computation if not). Note that messages should be considered identical even if the "reply" fields are different, as long as the semantics/computations they represent are identical in every other respect. Also note that the computation should be purely functional, i.e. free from side-effects, for the caching optimization suggested to work and not change the program semantics at all.

If what I am suggesting is not compatible with the Akka way of doing things, and/or if you see some strong reasons why this is a very bad idea, please let me know.

Thanks, Is Awesome, Scala

share|improve this question
add comment

2 Answers

What you are asking is not dependent on the Akka framework but rather it's how you architect your actors and messages. First ensuring that your messages are immutable and have an appropriately defined identities via the equals/hashCode methods. Case classes give you both for free however if you have actorRefs embedded in the message for reply purposes you will have to override the identity methods. The case class parameters should also have the same properties recursively (immutable and proper identity).

Secondly you need to figure out how the actors will handle storing and identifying current/past computations. The easiest is to uniquely map requests to actors. This way that actor and only that actor will ever process that specific request. This can be done easily given a fixed set of actors and the hashCode of the request. Bonus points if the actor set is supervised where the supervisor is managing the load balancing/mapping and replacing failed actors ( Akka makes this part easy ).

Finally the actor itself can maintain a response caching behavior based on the criteria you described. Everything is thread safe in the context of the actor so a LRU cache keyed by the request itself ( good identity properties remember ) is easy with any type of behavior you want.

share|improve this answer
add comment

As Neil says, this is not really framework functionality, it's rather trivial to implement this and even abstract it into it's own trait.

trait CachingExpensiveThings { self: Actor =>
  val cache = ...
  def receive: Actor.Receive = {
    case s: ExpensiveThing => cachedOrCache(s)

  def cacheOrCached(s: ExpensiveThing) = cache.get(s) match {
    case null => val result = compute(s)
    case cached => self.reply_?)(cached)
  def compute(s: ExpensiveThing): Any 

class MyExpensiveThingCalculator extends Actor with CachingExpensiveThings {
  def compute(s: ExpensiveThing) = {
    case l: LastDigitOfPi => ...
    case ts: TravellingSalesman => ...
share|improve this answer
I've also calculated the last digit of Pi, what did you make it to be ? ;p –  Noel Kennedy Nov 16 '12 at 15:08
The last one is π –  Viktor Klang Nov 17 '12 at 0:04
add comment

Your Answer


