Long tasks won't block

In this section, we'll improve the behavior of our application while using functionalities provided by third parties (like the one used so far, Twitter). Such services aren't always very efficient or may encounter some problems or maintenance.

The problem with that is its independence from our code, we don't have any control. While they help us for some parts of our application, these third parties can also break our performance. This impact comes from the fact that we're using them in actions that have to wait for an external request to respond or to fail with a timeout.

In such cases, our server can become stuck really quickly by waiting on a large number of third-party requests to release. But thinking further, we should be wondering why those actions are blocking our threads and preventing other requests being handled by the server? This makes no sense; the action, which is under the covers waiting for a remote procedure to end, should release the thread and wake up at some later point, that is, when the procedure has ended.

Actually, Play! Framework 2 is meant to work with a very small thread pool (usually the number of cores plus one), and that's why our server will slow down very quickly. But actually, it should slow very quickly in any case if we think that a request is always handled by a thread.

However, this is not how things are going on in Play! 2. Roughly speaking, the framework uses a loop to handle all requests where some can be inactive until background operations have been released. This loop iterates each time a thread is freed by another process.

So how can those threads be freed if the action hasn't finished yet? That's the point where the Promises come back. Let's have a quick overview on how it's done.

An action is a static method that returns a Result that will cause an HTTP response by the framework. Ok, but Result has a derived class, AsyncResult, which wraps Promise<Result> in it. This is the key point. When an action returns such a result, it has finished its process, or at least it has prepared it for a future result. As the method has returned, the thread can be freed up and made available, which means a new iteration that can take the next request or the next woken one. This is non-blocking!

Wow! That was intense. Let's now see how to create and return such an AsyncResult. In this case, Play! Framework 2 will also hide a lot of things for us, simplifying things. In order to create an AsyncResult object, we need to do the following:

  • Create an instance of Promise<Result>
  • Use async (Java) or Async (Scala)

Nothing else. What more could we ask for? Let's see how we can apply this in our Twitter controller:

Long tasks won't block

The only thing we had to do is to pass promisedResult to the async method, given that it's already Promise<Result>. We didn't even have to change the return type!

Now the Scala version (yes, we had put it aside for a while, but the code files include this version as well) is shown as follows:

Long tasks won't block

In the Scala version, we had to replace await.get with the variable itself. But we also passed the whole body of the function to the Async construct when we could have just wrapped promiseResult only.

Reloading our application and clicking on a username or a tag will leave the application unchanged at the user level. In fact, it turns out that these asynchronous functionalities can be used for any type of long running task and not only for web service calls. Indeed, a statistical call to a database can be time and resource consuming, so it would be worth defining such requests as asynchronous too. (Note that if, for instance, the database's driver is blocking, the request will block at the time the data starts arriving.)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset