Tackling the ask pattern and `timeout hell`

708 views
Skip to first unread message

Bartłomiej Szczepanik

unread,
Jul 27, 2014, 5:49:31 PM7/27/14
to akka...@googlegroups.com

Dear Hakkers,


In my current project I have experienced that ask pattern is a kind of awkward solution. I really like it for its elegance and readability but besides not very efficient implementation (spawning actor per each request) it introduces a phenomenon which I call `timeout hell`. It is really hard to introduce a consistent timeout for a user request when you use several ask requests in your pipeline.

 

I have also seen that many people discourage from using that pattern and encourage to replace it with actor per request pattern. Personally I don't see any difference with the ask implementation with a new actor spawned each time. It would be great if someone convince me why it is different and better.

 

I'm guessing the cause of this problem is not about the usage of the ask pattern itself but due to crappy, not reactive design of the system. I agree that the best is not to wait or block for anything. But currently I cannot use fully reactive pipeline with pushing events to GUI and so on. However, I'd like to block user only on HTTP request and be fully reactive in the rest of the pipeline. But still there is a problem. Let me describe it on a simple example.

 

Let's say the backend can handle ChangeColorOfItem command. In the old-style application we probably would write something like (pseudocode):

on(ChangeColorOfItem cmd) {

     item = itemRepository.getById(cmd.itemId)

     item.changeColorTo(cmd.getColor)

     itemRepository.save(item)

}

 

It's easy for me to translate it to akka using ask pattern. But as I said, I would like to avoid it. I came up with several possible solutions:

 

Fire and forget solution

 

The idea is to move all but first steps to recipients of respective messages. For example the repository command retrieves the requested item and sends message to the retrieved item basing on the passed context. I don't like the idea because of scattering of business logic and hindering the reusability of components.

 

Create an actor and split the business logic on multiple steps

 

Something more sophisticated. Here's some sample of code:

 

Receive = {

  match msg @ ChangeColorOfItem(itemId, color) => {

     itemRepository ! GetById(itemId, Context(sender, msg))

  }

  match RepositoryResponse(item: ActorRef, context) => {

     item ! ChangeColorTo(context.msg.color, context)

  }

  match event @ ColorChanged(itemId, color, context) => {

     itemRepository ! SaveEvent(event)

  }

  match ItemSaved(context) => {

      context.sender ! ColorSuccessfullyChanged(context.msg.id, context.msg.color)

  }

}

 

Advantages (comparing to ask pattern):

  • The entire use case logic is still in one place (one actor)
  • Timeouts handling moved on a higher level (e.g. REST interface level) what enables easy timeout management per entire use case
  • No problem with resource leaks when there is a timeout or heavy load (no stale future objects in memory, only lightweight message)

 

Disadvantages:

  • Need to pass context explicitly all the time
  • Logic is harder to read due to steps introduction (a little similar to use of callbacks)
  • Exclusive processing of steps that in fact don't need that

 

Different ideas

 

I was thinking about leveraging Futures for similar idea as above. I wanted to pass some kind of callback with the message and it's context to the next recipient. That allows me to keep code in one place (unfortunately in callbacks but still). However I think it is impossible to send a lambda in a message to the actor. Is that any workaround? If so, would you find this approach useful?

 

Generally I would prefer to use a solution that keeps the idea of "application/use case service" which orchestrates the fine-grained classes and encapsulates use cases in the code in exactly one place. If you think it's impossible in reactive approach, please explain that and give some alternatives. I'd be very grateful for that.


Best,

Bartłomiej


 

Martynas Mickevičius

unread,
Jul 30, 2014, 11:10:23 AM7/30/14
to akka...@googlegroups.com
Hi Bartłomiej,


It deals with the "timeout hell" that you have described with an introduction of per-request-actor. This would solve some of the disadvantages that you have outlined in your solution. Mainly you will not need to pass around context, because that can be saved in a per-request-actor.



 

--
>>>>>>>>>> Read the docs: http://akka.io/docs/
>>>>>>>>>> Check the FAQ: http://doc.akka.io/docs/akka/current/additional/faq.html
>>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
---
You received this message because you are subscribed to the Google Groups "Akka User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to akka-user+...@googlegroups.com.
To post to this group, send email to akka...@googlegroups.com.
Visit this group at http://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.



--
Martynas Mickevičius
TypesafeReactive Apps on the JVM

Adam

unread,
Jul 31, 2014, 10:08:25 AM7/31/14
to akka...@googlegroups.com
I was facing a similar decision a few weeks ago.

For now, I've went with the actor per request approach that Martynas mentions.
The actor basically looks like a series of receive methods and I use become to switch between them.
Each switch is done whenever I need to wait for an asynchronous call (usually when accessing the database).

I also tried the approach of using futures (via the ask pattern), but this indeed was harder to work with mainly because it was hard to troubleshoot based on error messages and becuase I kept having to disable or lengthen these timeouts while debugging.

Note that I don't copy context data between become calls, I use private members in the Actor (the blog post also mentions this).

Konrad Malawski

unread,
Jul 31, 2014, 4:22:33 PM7/31/14
to Akka User List
I though you should also have a look at different patterns one can use to implement waiting for something,
this has been discussed nicely in this thread: Waiting on multiple Akka messages.


--
>>>>>>>>>> Read the docs: http://akka.io/docs/
>>>>>>>>>> Check the FAQ: http://doc.akka.io/docs/akka/current/additional/faq.html
>>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user
---
You received this message because you are subscribed to the Google Groups "Akka User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to akka-user+...@googlegroups.com.
To post to this group, send email to akka...@googlegroups.com.
Visit this group at http://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.



--
Cheers,
Konrad 'ktoso' Malawski
hAkker @ Typesafe


Jeremy Pierre

unread,
Aug 1, 2014, 1:47:06 PM8/1/14
to akka...@googlegroups.com
The actor-per-request approach mentioned by Martynas is great because it allows you to rely on supervision and escalation for all your failures be they HTTP timeouts on blocking REST calls or otherwise.  I find, however, that this can get more complicated if you have a scatter/gather approach or even a simple dependency graph between the calls.  If you want something with an API that feels a bit more direct, specifically wrapping blocking APIs, I recently tackled this somewhat indirectly here:  

http://noisycode.com/blog/2014/07/27/akka-and-jdbc-to-services/  (starts from ask pattern and builds away from it)

Direct link to example project here:  https://github.com/j14159/akka-jdbc-post

That lets you use for-comprehensions, pipeTo or even just map/recover/etc where necessary.  Worth noting that failures within the actor you're delegating to in the above example can be propagated back to the client faster by simply sending them on the promise should you so desire.

Hope that helps,

Jeremy

Tim Pigden

unread,
Aug 2, 2014, 1:40:02 PM8/2/14
to akka...@googlegroups.com
consider fsm as an alternative to the multiple become actor. It's neater IMO when you get above 3 states.



Reply all
Reply to author
Forward
0 new messages