Sunday 27 November 2011

Parallel Ajax requests

In our last Web project (rather rich jQuery UI + Asp.Net MVC) we had one page where we needed to do several async Ajax calls to load different screen items (combos, jqGrid, several lists in one Accordion...) just on page load. All these calls were independent from each other (results from one of them were not needed in another of the calls). This led us to some interesting head scratching about which was the best approach:
  • Just launch them sequentially. Each call is started when the previous one finishes. This basic approach seems rather wise if the failure to retrieve some data means that the whole page turns invalid, in that case you stop you sequence of calls and show the error message.
    For this you just can queue your calls just by passing them as callbacks to the success event of your previous call. This is fine for a few calls, but for many of them it ends up being rather messy. A better approach here is the use jQuery's deferred objects to create your queue. You would use a chain of calls to the pipe method (the documentation is a bit confusing to me, but that's the way to go for chaining calls with deferred).
    Of course, you can always create you own Queue.
  • Just launch them all in parallel. Pretty simple from a code perspective (I would reccomend to wrap your "Loading..." modal dialog in some helper class with a counter that increases-decreases with each show-hide call, that way you don't need to care of when all calls have completed to turn the page operative, each call just calls to the Dialog show-hide and that's all)
  • Keep a limit on the number of concurrent calls. I mean, maybe you have to do 5 calls but don't want to have more than 2 concurrent calls. So you would start launching 2 calls and queuing the rest, and each time one call is done you would launch the next call in the queue. To my knowledge jQuery's deferred does not have an out of the box way to do this, so you would have to implement your own system.

So, what's the best way to proceed? I don't have a firm answer to that, so I'll just outline some of the many things that we should take into account.

  1. HTTP persistent connection. Almost all browsers and servers use this technique, so one same http connection is reused for requesting different resources. OK, good, but, what about requesting those resources in parallel? Let's move to point 2.
  2. Number of allowed concurrent requests in modern browsers (notice that though he's asking about Ajax requests, this applies to any kind of request). Seems like this ranges from 2 to 6. The number of concurrent Http connections to the same domain needs to keep a balance between what is good for the client (more connections) and what is good for the Server (don't flood it with too many connections, you know, the C10k problem...).
    From these 2 points, let's think that we have 3 persistent http connections open to the server, each one requesting data, does each request have to wait for its response, or we could send several requests in parallel through the same connection? This is called Pipelining (similar to processor pipelining, when several instructions in different steps run in parallel in the same processor core), so, jump to point 3.
  3. HTTP pipelining Long in short, unfortunately this interesting feature is scarcely supported by most major browsers.
  4. Your server technology also plays a role here. For example, when using Asp.Net, any request that make use of the Session object will get serialized, so, it'll be just the same as if we had done the requests sequentially. This is so because concurrent access to the Session object should be avoided, and the Framework is doing just that locking for us. With Asp.Net MVC it's assumed that all actions make use of the Session, and that means that we can't have 2 actions running in parallel (so forget your concurrent Ajax requests from being really parallel, you'll see them both running in firebug, but on the server side they'll run sequentially). Hopefully this can be changed at the controller level, by means of this attribute: SessionState
    Read this article thoroughly for a much better explanation

No comments:

Post a Comment