1

I am new to Scala and Akka but have a simple scenario... I am attempting to take a list of urls, sending each url as a message into a new actor that will make an http GET request. With only a list of 16 urls, I get about 40-65% successes with the others getting dead-letter "message was not delivered". However, if I put a Thread.sleep(50) before creating the actor(s), I don't get the dead-letter misses. Note: the HTTP Get is using a java class, not a scala class which may or may not be a factor. See code example below... I know Akka doesn't guarantee messages, but this success rate doesn't look right, I must be doing it wrong. Thanks for any pointers.

  def ParallelTest(urls: List[String]): String =   {
     val system = ActorSystem("HelloSystem")

     var cnt: Int = 0
     for (item <- urls){
       Thread.sleep(50)
       createActor(system, "helloactor" + cnt, item)
       cnt += 1
       /*
       historical example
       helloActor ! "hello"
       helloActor ! "hello"
       helloActor ! "Buenos dias"
       */
      }
     system.shutdown

     println("parallel done");
     "done"   
   }

  def createActor(actorSystem: ActorSystem, actorName: String, urlItem: String) = {
    val helloActor = actorSystem.actorOf(Props[HelloActor], name = actorName)
    helloActor ! UrlTransport(urlItem)
  }
mytwocents
  • 847
  • 17
  • 29
  • 3
    I am pretty sure that shutting down the system right away is your problem. – sberry Feb 06 '14 at 23:51
  • To test that theory, move your `Thread.sleep` after the for loop and increase it to 2000 or so. I am guessing you will also get completion. – sberry Feb 07 '14 at 00:08
  • as pointed out by @sberry there is pretty much no doubt that your actor system shuts down too early before actors had a chance to receive or process a message. There are many ways to do this in parallel either using Actors or Futures or some reactive framework like RX. – yǝsʞǝla Feb 07 '14 at 03:16
  • As everyone has said you are shutting down the system early. I would also warn against using your for-loop. Remember that createActor doesn't block, so you have no control over how many actors are making get requests at once. While your application may be small enough that this might not matter it could haunt you when you scale to more get requests: http://stackoverflow.com/questions/21054434/controlling-spawning-of-actors-in-akka-who-consume-noticeable-amounts-of-memory – Andrew Cassidy Feb 07 '14 at 16:12
  • Indeed - moving the Thread.sleep(2000) outside of the loop, before the system.shutdown seems to have fixed this. Now that I see the problem, I can refactor - thanks! – mytwocents Feb 07 '14 at 16:13

1 Answers1

2

You may want to consider doing something like this, having a manager for the actors to know when they are done.

class HelloManager(urls: List[String]) extends Actor {

    var completed = 0
    def remaining = urls.size - completed

    def receive: Receive = {
        case StartSystem => startRequests
        case RequestComplete => handleComplete

    }

    def startRequests(): Unit = {
        for ((url, i) <- urls.zipWithIndex) {
            val helloActor = context.actorOf(Props[HelloActor], name = s"helloActor$i")
            helloActor ! UrlTransport(url)
        }
    }  

    def handleComplete(): Unit = {
        completed += 1
        if (remaining == 0) 
            // do something like
            // context.stop(self)
    }
}

Then you just need the HelloActor to do a sender ! RequestComplete when it is done with its job.

sberry
  • 128,281
  • 18
  • 138
  • 165
  • nice solution, but it seems to have a problem that it does not handle sudden actor death or timeout. One option would be to use an ask `?` with timeout perhaps. – yǝsʞǝla Feb 07 '14 at 03:17
  • @AlekseyIzmailov: In the system we use at work (a lot of akka) we frown upon asks since they block. If sudden actor death is a concern I would use a supervisor. Timeouts can be handled by having the HelloActor start a scheduler that sends a Timeout case class and allows the actor to report back to sender that there was a timeout so the Manager can respawn a new actor in its place. – sberry Feb 07 '14 at 04:13
  • I personally prefer 'tell' vs 'ask' for performance reasons and semantics, but 'ask' does not really block. It returns a Future that might be complete or not. Your code might block afterwards if it's awaiting on the result of this Future, but it does not have to. The reason I suggested 'ask' here is because supervising and using DeathWatch, etc seemed like an overkill for this application since it needs to collect all results and shutdown the system right after. In this case 'ask' looks like a better way of doing it. – yǝsʞǝla Feb 07 '14 at 04:28
  • True, ask does not implicitly block until await is used. But I can't really think of a situation where you would ask and not await at some point. Perhaps it is overkill... but it's what I am used to at this point. :) – sberry Feb 07 '14 at 06:53
  • One situation would be to pipe the Future back to the client. Akka will take care of awaiting in non-blocking manner so the whole chain of calls will be non-blocking. In fact many frameworks work with results produced as a Future and are capable to send them over to the client without blocking. You can simply install a callback on the Future to achieve that. – yǝsʞǝla Feb 07 '14 at 14:06
  • Instead of context.stop(self) I am using "context.system.shutdown" – mytwocents Feb 07 '14 at 19:22
  • I think you might need a `this.synchronized()` block for `completed` variable every time you want to modified it. It's a shared variable by multiple actors. – Wei Lin Nov 29 '16 at 23:48