Re: [play-framework] does promise.get() allow other requests to run on the same thread while it waits?

1,057 views
Skip to first unread message

Mathias Clerc

unread,
Jun 15, 2012, 4:32:35 AM6/15/12
to play-fr...@googlegroups.com
I think by blocking until you get a response you are blocking the server.

2012/6/15 Jonathan Stray <jonath...@gmail.com>:
> Our application has to do an HTTP request when the user wants to create a
> new set of documents. Following the documentation, we've done:
>
> return async(
>      WS.url(documentCloudQuery).setQueryParameter("q",
> queryString).get().map(
>      new Function<WS.Response, Result>() {
>      public Result apply(WS.Response response) {
>      JsonNode documentReferences = response.asJson().get("documents");
> ...
>
> But, looking at the Promise<> interface, I was able to write this much
> simpler code, which also works:
>
> Promise<WS.Response> httpreq =
> WS.url(documentCloudQuery).setQueryParameter("q", queryString).get();
>
>     WS.Response response = httpreq.get(); // blocks until request
> completes
>
> JsonNode documentReferences = response.asJson().get("documents");
>
> My question is, does this allow the server to service other clients on the
> same thread while waiting for the response? My understanding is that Play
> keeps a pool of N+1 threads for N cores, and uses these async waiting points
> to allocate the same thread to other clients while the first request is
> waiting on a response from elsewhere. Do I need to use the async/callback
> method to make this work, or is promise.get() fine?
>
>    - Jonathan
>
> --
> You received this message because you are subscribed to the Google Groups
> "play-framework" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/play-framework/-/egckmYkCbQAJ.
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to
> play-framewor...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/play-framework?hl=en.

Steven Wong

unread,
Aug 30, 2012, 11:54:42 AM8/30/12
to play-fr...@googlegroups.com
I also have the same question. Especially when you have to perform more actions on the result from the WS call before returning a result to the user?

AsyncResult won't allow you to do that so you're left with blocking the server.

On Tuesday, July 10, 2012 6:39:31 AM UTC+10, Aldrion wrote:
Hi, my questions pertains to this exact problem Jonathan has introduced, but from a different angle and I haven't been able to find a solution (at least not of the kind that would enlighten me) online. Namely, while it is self-evident, how you get to a JsonNode (or any other data) in the second Jonathan's example, I can't seem to get my head around comprehending, how do you get a JsonNode or any other data out of the async() method through Java libraries? All the examples I have come across only deal with returning a Result with an ok() method.

Using F.Promise's get() blocks the thread (or at least so I understand) and as such obviously negates one of the Play!'s fortes, while async() as per my understanding only takes Promise<Result> as an argument and returns AsyncResult exclusively, and neither Result nor AsyncResult seem to offer any usable method. Is there any way to collect/fetch data from Result or AsyncResult that I am not aware of? Or is there an altogether different way of tackling this?

Any advise would be greatly appreciated...

Steven Wong

unread,
Aug 31, 2012, 1:49:55 AM8/31/12
to play-fr...@googlegroups.com

James Roper

unread,
Aug 31, 2012, 2:10:33 AM8/31/12
to play-fr...@googlegroups.com
I think you've fundamentally misunderstood how promises work.  A promise says "at some point in future, I promise I will make one of these available."  In the case of the WS API, that is a WS.Response, ie Promise<WS.Response>.  When returning an async response to Play, you want to return a promise for a result, ie Promise<Result>.  You can convert a Promise<WS.Response> to a Promise<Result> by calling the map function, which accepts a function that takes a WS.Response and returns a Result.  This isn't invoked immediately, it's invoked when the first promise makes the WS.Response available.  You could map it to something else first if you really wanted, and then map that thing to a promise, eg:

Promise<WS.Response> responsePromise = WS.url(...).get();
Promise<Foo> fooPromise = responsePromise.map(new Function<...> { public apply(WS.Response response) { return Json.fromJson(response.asJson(), Foo.class); }};
Promise<Result> resultPromise = responsePromise.map(new Function<...> { public apply(Foo foo) { return Result(foo); }};
return async(resultPromise);

But that's a bit unnecessary, because you don't need the intermediate fooPromise, you could put your code that converts to/from json in the first map function.  This is also, if you were to call other libraries, where you would put all that code.  If you want to do another async operation (eg two WS calls), then you can use flatmap:

Promise<WS.Response> responsePromise = WS.url().get()...
Promise<WS.Response> secondResponsePromise = responsePromise.flatMap(new Function<...> { public apply(WS.Response response) { return WS.url(..).get(); }};
Promise<Result> resultPromise = secondResponsePromise.map(new Function<...> { public apply(WS.Response response) { return Ok(...); }};

The thing to be aware, is once you start working with Promises, you can never directly work with the result, everything gets done in map() and flatMap().

There's a keynote presentation and example code here that I did for a Play user group:

Ben McCann

unread,
Sep 2, 2012, 5:43:00 PM9/2/12
to play-fr...@googlegroups.com
Hi James,

Do you happen to have a video or slides from that talk you gave?  I have a very poor understanding of what should be wrapped in a promise and the docs are quite anemic on the topic.  E.g. should all of my calls via the Mongo Jackson Mapper be wrapped in promises?  Or just external HTTP requests?  How long should an action take before it is recommended to wrap it in a promise?  Why not just wrap every single request in a promise?  I'm having a really hard time understanding what tradeoffs are in play here.

Thanks,
Ben

Nico

unread,
Oct 3, 2012, 3:37:22 PM10/3/12
to play-fr...@googlegroups.com
+1 to see slides or other materials :-)

And I also wonder why every actions are not wrapped into a Promise / async by default ?..

CB

unread,
Oct 4, 2012, 1:53:01 AM10/4/12
to play-fr...@googlegroups.com
+1 on the slides

James Roper

unread,
Oct 4, 2012, 6:42:47 PM10/4/12
to play-fr...@googlegroups.com
Hi Ben,

Sorry, I completely missed this message a month ago.

There's no strict rules here, multiple solutions will work well, and it's very dependent on the profile of your app.

I don't have a video from the talk, there are keynote slides in the github repository but there's not much in them.  I will probably be giving this talk again in the not too distant future at a conference that does record video, and it will go into more detail and I'll produce more comprehensive notes for it, so you'll be able to see it then.

I think a good start is that anything that relies on third party services should be asynchronous.  There's nothing worse than your app going down because some other system completely outside of your control is not responding and tying up all your applications threads while you try to make calls on it.  Calls to internal systems in your company are also good to make asynchronous, and I would always do that, means my services can't bring each other down so easily.

When it comes to talking to databases, for simple lookups that are entirely from indexes, then I'd say unless you have an asynchronous driver for that database, then it's probably not necessary to do things asynchronously.  The exception to this is if you need to make many database calls per request, then doing them in parallel is a good idea.  If you've got big queries, map reduces etc, then this may be good to do asynchronously too.  And if you're doing lots of all of the above, then take a look at Akka, because with Akka you will have a lot more control over how many threads are devoted to doing what.  Klout did a great blog post about this: http://corp.klout.com/blog/2012/10/scaling-the-klout-api-with-scala-akka-and-play/

The final thing to be said, if you're doing Java, using promises or any sort of async stuff has a huge boiler plate overhead, and this needs to be considered, if your simplest code paths are just one anonymous class after another, then you are making large maintainability/code readability trade offs, and it might not be worth it.  If doing Scala on the other hand, working with Promises is no harder than working with Options, and so from a code perspective, as long as you're comfortable with the concepts of map and flatmap, there's no disadvantage to using promises.

Cheers,

James

James Roper

unread,
Oct 4, 2012, 6:49:12 PM10/4/12
to play-fr...@googlegroups.com
In actual fact, every action is wrapped in a promise/future, when Play receives a request, it passes it off to Akka to handle asynchronously.  But as to whether your code in the action is asynchronous, well Play has no control over that, that's up to you.  The reason why Play doesn't force you to return a Promise from every action is to make the simple case simple, otherwise every action where you didn't make some call into the promise API you'd have to wrap your result in Promise.pure().

Ryan Means

unread,
Oct 5, 2012, 3:40:58 PM10/5/12
to play-fr...@googlegroups.com
Due to the very reason of what James Roper mentioned in the amount of boiler plate code for Java we're staying completely non-async for now. Ended up following James Ward's blog on how to tune a Play 2 app for non-async (James works for Heroku) http://www.jamesward.com/2012/06/25/optimizing-play-2-for-database-driven-apps. All of our data comes off an in-house rest service from our CRM that is sitting directly on a DB. As much as we would have liked to go async, the Java code was going to be extremely thick as even our webapp text (which we cache in memcached) comes out of our CRM. Over time, we will start migrating our slower methods (methods that don't return within 1 sec) to async.

I would like to point out one very glaring issue that James Ward did not mention though - even though you've "tuned" the environment, as James Roper points out ALL requests are handed off to the Akka handler - from our testing - this appears to include static content! So say you have 20 threads, and all 20 are blocked at the moment, then 1 thread finishes and sends the result to the browser - the browser loads the HTML and sends requests back to the play server to get the static content referenced in the dynamic html output from play.. well, now your simple static content requests may be blocked waiting on those blocking akka calls to finish before it can return your simple static content! We experienced this in our testing using firebug and gatling. We were noticing that while running a gatling instance against our blocking app, we could load pages (with a wait time) but then noticed that the static resources in firebug were also blocking instead of being immediately returned! To fix this, we ended up going to a front-end apache server so play only deals with dynamic content now. If you went' all async, you wouldn't have to worry about this as Play encourages you to do. Ultimately, it would be nice if Play had a separate akka thread pool for static content so you never would be at risk for this happening.

If I'm wrong on any of this - please correct me! :-)

James Roper

unread,
Oct 7, 2012, 9:11:40 PM10/7/12
to play-fr...@googlegroups.com
No you're right on this.  What we probably should do is have a few different configuration profiles documented that a user can use.  Out of the box, Play is tuned for async.  We should make it very easy for the user to switch to a more traditional model, such as what most servlet containers use, where there is one large (150 threads) thread pool that handles all requests.

Pascal Voitot Dev

unread,
Oct 7, 2012, 10:12:46 PM10/7/12
to play-fr...@googlegroups.com

Just for info, with scala2.10 and the new futures will come the ability to separate execution contexts based on threadpools, thus allowing in theory separating HTTP threads from akka threads from jdbc threads etc...

Pascal

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To view this discussion on the web visit https://groups.google.com/d/msg/play-framework/-/IaItKa68ZWMJ.

Yann Simon

unread,
Oct 8, 2012, 4:52:37 AM10/8/12
to play-fr...@googlegroups.com
As far as I understand, two threads pools should be used:
- one for all computations based on asynchronous IO,
- and one for CPU intensive tasks

The thread pool for IO could be used for static content, ahc...
The thread pool for CPU intensive tasks could be used for controllers.

Is it the actual configuration?

The non asynchronous stuff is still problematic to handle. It is the
typical problem of JEE applications, where we can only use a high
number of threads, leading to context-switching.
Therefore, all synchronous stuff should maybe handled by a separate
thread pool, which destiny is not to exist... :)

Yann

2012/10/8 Pascal Voitot Dev <pascal.v...@gmail.com>:

Ryan Means

unread,
Oct 8, 2012, 9:12:35 AM10/8/12
to play-fr...@googlegroups.com
Thanks for the response James! Even though there are some gray areas right now when trying to do blocking style apps - we still believe that Play is the best web development framework out there for Java development shops - you just need to figure out how to work around things like async and understand the ramifications (such as what happens to your static content). Thanks for the good work - I'm excited to see what Play will look like one year from now..

James Ward

unread,
Oct 8, 2012, 5:03:27 PM10/8/12
to play-fr...@googlegroups.com
Serving all of your static content direct to users from Play is simple but I think a better solution is to proxy the static content requests through a caching CDN or proxy server.  Setting things up so that deployment is still simple, is pretty easy to do with services like Amazon CloudFront.  Here is another blog post I did that walks through how to do that:

Alternatively you could setup a Squid or Varnish server in front of your Play app that will serve your static assets.  The benefit of this (or the CloudFront) approach is that everything is still in one app but most of the requests hit a cache (preferably edge-cached) tier.  Then you just need to provide a way to configure your app to use different URLs for the static assets.  Play 1 had a way to do this and I'd love to create a nice way to do this in Play 2.

-James
Reply all
Reply to author
Forward
0 new messages