Akka Ask Antipattern

Yurii Gorbylov
4 min readAug 22, 2018

--

In this post, I’d like to raise a topic that relates to ask pattern in Akka. How it works and what problems might it cause.

What is ask pattern?

The usual way of communication between actors in Akka is tell pattern with a fire-and-forget semantic. It means that an actor fires a message to another actor and doesn’t wait for a response. To receive a response, the second actor should fire a message back.

Alternatively, ask pattern provides a way to send a message to an actor and expect to receive a response from it. The result of ask will be a Future that represents a possible reply.

Under the hood, the ask operation involves creating a listener actor for handling this reply. The listener actor is quite cheap, but not free. Also, it has a timeout to prevent leak of resources.

Now, suppose that we have a series of five actors. Each actor performs some small task and then passes the result to the next actor in a pipeline. In the end, we want the original actor to receive the result of the processing. This will look like:

Problem 1. Resources wasting

The flow looks more complex than we really need. In the chain of 5 actors, we have created 4 actor listeners and passed 8 messages. It happens behind the scenes, but it still consumes resources. Can we avoid that? Yes. Just use the following scenario:

As you may see, we replaced most of the ask with forward and ended up with 0 listeners and 5 message passes.

What forward actually does? forward is a special version of tell, which keeps original message sender. Therefore Actor1 will be recognized as a message sender, as long as actors are passing the message using forward. Thanks to that, Actor5 can send a reply directly to Actor1, as otAer actors are not necessary anymore.

Problem 2. Handle total timeout

In a long chain of actors that are using ask it is difficult to set the right value for a timeout so that various timeouts work together in a sensible way. What really necessary is that requester actor Actor1 succeeds within the specific timeout. All the other timeouts are irrelevant. But, because we have overused the ask pattern, we are forced to define timeouts at each stage of the process.

Let’s assume that the entire request needs to happen within 30 seconds and we evenly distributed total timeout between actors. What will happen if:

  • A new actor has been added to the chain of actors
  • A requirement on timeout for an actor of the chain has changed
  • A requirement on overall timeout has changed

The answer is pain will happen. You have to review and edit timeout for every actor in the chain.

Problem 3. Handle response

The main problem here is that ask returns a reply as Future[Any], so you should handle the incoming message in future’s callback out of Receive function. It can happen in parallel with running receive code, therefore there is lots of danger there. You can corrupt actor’s state or cause hard-to-reproduce bugs.

Conclusions

The point of the article is to realize that ask pattern designed to be used only in very specific situations:

  • You need to communicate with an actor from outside the system
  • You need to use a request/response–style approach and a timeout is desirable

So, always prefer tell, and only ask if you must.

--

--