Search This Blog

2011-11-21

Controlling Ajax requests flow

While developing rich internet applications which contain a lot of Ajax requests, sooner or later you come to the idea of controlling requests flow. Users are continuously interacting with site(s) GUI that causes massive Ajax requests which are passed through load balances, firewalls, virtual servers etc. Problem might happen on any layer - from dead lock in database to exceptions in firewall; at the same time, user does not know and care about all that complex issues on the background. 
There are several ways to manage Ajax requests flow and I will show on of them: controlling request queue with JavaScript and jQuery.
The idea is not to bomb overloaded server with tons of requests. Instead, we assume if requests are processed to long - there is some issue on the server side and we should react somehow. With decribed approach we are organizing the queue of requests, but quick ones can still be processed simultaneously.
First, let's synthesize idea with tests. Let's imagine we have some web service that processes one request per two seconds. Our web client is continuously sending requests to the service.
module("Test.RequestController.js");

asyncTest("Test: sending requests", 6, function () {
  var url = "/TestWebService";
  var data = { "testData": "data" };

  var controller = new RequestController();
  controller.ajax({'url':url,'data':data}).done(function () {
    ok("done1");
  });
  controller.ajax({'url':url,'data':data}).done(function () {
    ok("done2");
  });
  setTimeout(function () {
    controller.ajax({'url':url,'data':data}).done(function () {
      ok("done3");
    });
    controller.ajax({'url':url,'data':data}).done(function () {
      ok("done4");
    });
  }, 1000);
  setTimeout(function () {
    controller.ajax({'url':url,'data':data}).done(function () {
      ok("done5");
    });
    controller.ajax({'url':url,'data':data}).done(function () {
      ok("done6");
      start();
    });
  }, 2000);
});
Our test service has two seconds delay. But, let's say after tree seconds delay, user should be warned about the problem and/or ajax requests should be redirected. We can wrap jQuery.ajax function with our Request controller which encapsulates required logic.
First, let's define constructor with preset data.
RequestController = function () {
  this.requestCounter = 0; // Number of too long requests
  this.timeout = 3000; // Default timeout for "long" requests - 3 sec
  this.maxLongRequests = 3; // Max amount of simultaneous long requests 

  this.requestPromise = jQuery(this).promise();
};
Then we should wrap jQuery ajax with our queue:
RequestController.prototype.ajax = function(ajaxParams) {
  var ajaxRequest = function() {
    var isLongRequest = false;

    var ajaxPromise = jQuery.ajax(ajaxParams).always(function() {
      if (isLongRequest) {
        this.requestCounter--;
      }
    }.bind(this));

    setTimeout(function() {
      if (ajaxPromise.state() == "pending") {
        this.requestCounter++;
        isLongRequest = true;
      }
    }.bind(this), this.timeout);

    return ajaxPromise;
  }.bind(this);


  if (this.requestCounter >= this.maxLongRequests) {
    // show warning to the user
    // and warn system admin or redirect request
    return this.requestPromise = this.requestPromise.pipe(ajaxRequest);
  } else {
    return this.requestPromise = ajaxRequest();
  }
};
The idea is simple enough: we have a wrapper function that calls jQuery.ajax; on request complete/fail we check: if request was too "long" we just decrease the pending requests counter. On setTimeout function we are checking whether request is finished; if not - request can be considered as long: we increase the counter and switch the flag of long request.
Finally, we check whether flow reached maximum allowed number of long requests: if not - just return request promise to the caller. If it reached - we can send a warning and put request to the queue described earlier.
The request flow look like this:


2011-11-17

Slavic mentality: laugh or cry


I would like to share an incredible story I have observed in my life. 
The story took place in the early 90s, during the high point of turmoil in Ukraine, and at that time I was studying in the elementary school. 
I lived in a long block of flats, together with my grandparents. Despite the large number of tenants, the people were generally polite and friendly except for some... strange neighbors. As you might guess, they are the heroes of the story.
My neighbors from the eighth floor were common workers on a confectionery. Those were difficult times so people were often paid in barter. Neighbors were lucky and received a lot of candies for their job and that is why I knew them :-). 
In order to protect the property they also had a German shepherd called Nayda. Nayda was a good and kind dog, but to the great misfortune, by breaking all rules, it was fed with chocolates earned by her hosts... Nayda was really a fat dog and weighed over 60 kg because of such a diet. Nayda lived 6 years of a sweet life. 
It was quiet and warm spring holiday, the grass was green, the birds were singing and people enjoyed with a nice weather. Nayda died that day. It was so sad... and neighbors laid it to rest according to our customs, accompanying the ritual with drinking vodka...
After the third bottle a “brilliant” idea was born in their heads - to bury the dog on our backyard. The problem is that the dog was heavy and the block house was so long that they would have to drag the body of about 400 meters to get around it. Their next brilliant idea was: "Well, Nayda is already dead so we can throw the body off the balcony and gravity will do everything for us!" Damn, how did they come up with this?!
Without any hesitations, they dragged the body onto the balcony and threw it through a window... Then they just got dressed, took a shovel and went out to dig a grave; but when they got to the point they discovered there was no dog! It just disappeared! So they started looking for a dog in the yard, swearing at the same time.
The real reason they realized a little later. 
Some balconies in apartment houses were equipped with special rails with stretched ropes that stick out two feet from the outside balcony; they are designed for drying clothes.
It turned out that the dog's body collided with ropes in flight in front of the third floor and jumped inside into the open window... OMG
The neighbor from the third floor was at home that day and he heard some strange noise, so he decided to find out the reason. When he opened the door of the balcony - he was stunned by what he saw...
When he collected his thoughts together and looked out of the window - he saw drunken neighbors, who were calling for their dead dog. 
After long abuses and battering, other neighbors called the police to investigate the situation. When police arrived, the house turned into a loony bin: people behaved inappropriately and evidences were incredible. However, finally, experienced police officers of the Soviet hardening dragged the body of the dog in the yard and buried. Drunken neighbors had been fined for disturbing public order, but the moral injury of the neighbors on the third floor has not healed to this day.