Tell, don't ask and completing RequestContexts

4,094 views
Skip to first unread message

Ian Forsey

unread,
Feb 20, 2013, 6:55:02 PM2/20/13
to spray...@googlegroups.com
Hey there,

I really enjoyed the podcast about spray over at http://scalatypes.com/ and specifically the bit about how preferring actor tells over asks can be more flexible. I was wondering if anyone had gone with this approach for their own applications that use spray with regards to completing requests. All the examples I've seen of spray routing (in the docs, on this mailing list etc)  use asks from the routing layer into the application actors and then complete with the returned Future in the routing layer. Is it a viable approach to instead use tells and pass the RequestContext along all the application actors until you get to a point where you can call complete the RequestContext somewhere deeper, actually within the application actors.

I saw [this][1] thread where Age mentions this approach along with encapsulating the RequestContext within a custom class to prevent RequestContexts bleeding into all the application code, but I was wondering if anyone is using this approach? Where do you guys complete your requests? With futures in the routing layer or with messages in the application actors?
 
Thanks

Ian

Mathias

unread,
Feb 21, 2013, 3:49:30 AM2/21/13
to spray...@googlegroups.com
Ian,

this is a good question, which deserves some more detailed explanation:

In the end the only thing that really completes the request is a call to `requestContext.complete`. Thereby it doesn't matter which thread or Actor context this call is made from. All that matters is that it does happen within the configured "request-timeout" period.
You can of course issue this call yourself in some way or another, but spray gives you a number of pre-defined constructs that maybe fit your architecture better than passing the actual RequestContext around. Mainly these are:

1. The `complete` directive, which simply provides some sugar on top of the "raw" `ctx => ctx.complete(…)` function literal.
2. The Future Marshaller, which calls `ctx.complete` from an `future.onComplete` handler.
3. The `produce` directive, which extracts a function `T => Unit` that can later be used to complete the request with an instance of a custom type.

Architecturally, in most cases, it's a good idea to not have the API layer "leak into" the core of your application. I.e. the application should not know anything about the API layer or HTTP. It should only deal with objects of its own domain model. Therefore passing the RequestContext directly to the application core is mostly not the best solution.

Resorting to the "ask" and relying on the Future Marshaller is an obvious, well understood and rather easy alternative. It comes with the (small) drawback that an ask comes with a mandatory timeout check itself which logically isn't required (since the spray-can layer already takes care of request timeouts). The timeout on the ask is required for technical reasons (so the underlying PromiseActorRef can be cleaned up if the expected reply never comes).

Another alternative to passing the RequestContext around is the `produce` directive (e.g. `produce(instanceOf[Foo]) { completer => …`). It extracts a function that you can pass on to the application core. When your core logic calls `complete(foo)` the completion logic is run and the request completed. Thereby the application core remains decoupled from the API layer and the overhead is minimal. The drawbacks of this approach are twofold: first the completer function is not serializable, so you cannot use this approach across JVM boundaries. And secondly the completion logic is now running directly in an actor context of the application core, which might change runtime behavior in unwanted ways if the Marshaller[Foo] has to do non-trivial tasks.

A third alternative is to spawn a per-request actor in the API layer and have it handle the response coming back from the application core. Then you do not have to use an ask. Still, you end up with the same problem that the PromiseActorRef underlying an ask has: how to clean up if no response ever comes back from the application core? With a re-request actor you have full freedom to implement a solution for this question. However, if you decide to rely on a timeout (e.g. via `context.setReceiveTimeout`) the benefits over an "ask" might be non-existent.

Which of the described solutions best fits you architecture you need to decide yourself.
However, as I hopefully was able to show, you do have a couple of alternatives to choose from.

Cheers,
Mathias

---
mat...@spray.io
http://spray.io

On 21.02.2013, at 00:55, Ian Forsey <for...@gmail.com> wrote:

> Hey there,
>
> I really enjoyed the podcast about spray over at http://scalatypes.com/ and
> specifically the bit about how preferring actor *tells* over *asks* can be
> more flexible. I was wondering if anyone had gone with this approach for
> their own applications that use spray with regards to completing requests.
> All the examples I've seen of spray routing (in the docs, on this mailing
> list etc) use *asks* from the routing layer into the application actors
> and then *complete* with the returned Future in the routing layer. Is it a
> viable approach to instead use *tells* and pass the RequestContext along
> all the application actors until you get to a point where you can call *complete
> *the RequestContext somewhere deeper, actually within the application
> actors.
>
> I saw [this][1] thread where Age mentions this approach along with
> encapsulating the RequestContext within a custom class to prevent
> RequestContexts bleeding into all the application code, but I was wondering
> if anyone is using this approach? Where do you guys complete your requests?
> With futures in the routing layer or with messages in the application
> actors?
>
> Thanks
>
> Ian
>
> [1] https://groups.google.com/forum/?fromgroups=#!topic/spray-user/xsqUAbONkGo
>
> --
> You received this message because you are subscribed to the Google Groups "spray-user" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to spray-user+...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

Ian Forsey

unread,
Feb 21, 2013, 5:37:01 AM2/21/13
to spray...@googlegroups.com
Thanks for your detailed explanation Mathias,

I really liked the idea of the actor per request approach, but as you said, if those actors in the api layer never receive a tell from the application to complete the request, then you need to clean them up or they will be hanging around forever waiting for that tell that will never come. And I can see if this cleanup of actors is done with a timeout, then it isn't really any different from using an ask to bridge the api and application layers.

I think for the meantime we are going to still use asks from the api layer to the application layer, but then once inside the application layer try to use tells over asks where possible and see how that goes.

Thanks again.
Reply all
Reply to author
Forward
0 new messages