Home > Net >  Aggregating multiple child actor responses in Akka Typed
Aggregating multiple child actor responses in Akka Typed

Time:04-07

I'm currently porting an Akka Classic app to Akka Typed. I have the following components:

  • HttpService - Not an Actor
  • JobDispatcher - An Actor
  • JobWorker - Child actor of JobDispatcher

The JobDispatcher is a singleton actor that orchestrates jobs. Each JobWorker is responsible for one "job" and knows the job's status.

The HTTP service will make an Ask to JobDispatcher, called GetJobStatuses. The JobDispatcher will then Ask each of the JobWorkers what their status is, aggregate the results into a list, and reply to HttpService.

The way I did this in Akka Classic was to have JobDispatcher do all the Asks, put the Futures into a list of Futures, and then transform that into a Future of Lists, and when that aggregate Future completed, I would send the results to HttpService. It looked something like this:

  val statusFutures: Seq[Future[JobStatus]] = jobWorkers map (jobWorker => (jobWorker ? GetJobStatus).mapTo[JobStatus])
  val aggregateFuture: Future[Seq[SearchStatus]] = Future.sequence(statusFutures)

  val theSender = context.sender()
  aggregateFuture onComplete {
    case Success(jobStatuses: Seq[JobStatus]) => {
      theSender ! jobStatuses
    }
    case Failure(exception) => {
      theSender ! exception
    }
  }

So, now that we're moving to Akka Typed, we're not supposed to use Futures / onComplete, but instead turn the Ask response into a message back ourself (JobDispatcher in this case). This is fairly straightforward for simple situations where I'm Asking one other actor for one reply. But in this case, I have a whole list of child actors from which I need to compile their responses.

The only thing I can think of is to make JobDispatcher hold a "state" of a list of JobWorker responses that I'm waiting for, track of which ones have been received, and when I've received them all, send a response message back to the HTTP service. This is further complicated by the fact that I may have multiple simultaneous Asks coming in from the HTTP service, and I'd have to track multiple copies of this "state", and somehow identify which HTTP request each one is for.

This is all WAY more complicated than the the aggregate Future solution above.

What is the simple/correct way to handle situations like this in Akka Typed?

CodePudding user response:

The docs suggest using a per-session child actor for this situation. The child actor, being only associated with a single HTTP request is implicitly tracking exactly one copy of that state and is also able to manage the state of the process of scatter/gathering jobs (e.g. around timeouts and retries).

It's also worth noting that the example classic code has a massive bug: never call sender in code involving futures. Mixing futures and actors is superficially easy but also easy to turn into something that only works by coincidence (with tests often exhibiting that coincidental behavior).

  • Related