2

I have received multiple reports from users of a Web-based GIS application we have rolled out one to two year ago, complaining the application becomes unstable/unresponsive. After a brief investigation, I found that requests from the third party Map API take up most available connections of browsers (pulling dynamic layers, they are images), and the processing time of these requests were prolonged due to a large number of users (1xx vs 1x as planned), so the requests from our own API had to wait for free connections and eventually timeout, the overview of such a situation is like this:

requests from the Map API (busy) | requests from our own API to get data from DB to display (waiting) | requests from our own API to post data to DB (waiting)

** A FEW MOMENTS LATER **

requests from the Map API (still working...) | requests from our own API to get data from DB to display (timeout) | requests from our own API to post data to DB (timeout)

what I want to achieve is moving any requests from our own API to get data from DB in front of requests from the Map API, however, are no way to tell the Map API it should wait for (more important) requests from our own API before they could update the map. I did came across this question and planned to propose a solution: open a socket before initializing the Map API, and requests from our own API to get data will go through it instead of using ajax. I wonder...

Should I also make the requests from our own API to post data to DB also using the socket? As rewrite API work with Ajax to Socket takes time, and users should post updates after they get the data and the updated map.

Also, making every request from our own API via socket seems moves the request congestion from Ajax to the socket.

2

The short answer is connection pooling. You need to have a pool of XMLHttpRequest objects that are used for sending AJAX requests, and track which ones are in use and which ones are free. As code needs to send a request, give it a free connection.

But this is JavaScript.

And there is a 3rd party library involved.

The long answer is the "short answer" plus a little Chicken Voodoo Magic™

The 3rd party code is likely sending AJAX requests by instantiating a new XMLHttpRequest object directly, or going through yet another library like jQuery — $.ajax(...) — which just uses XMLHttpRequest under the hood. Thankfully the common element is the XMLHttpRequest class, which is a member of the window object.

  1. Write a class for pooling connections

  2. Write a proxy class for XMLHttpRequest with the same public interface (properties included) that asynchronously gets a free XMLHttpRequest object from the pool

  3. Assign window.XMLHttpRequest to a local variable within a function closure wrapping your entire connection pool library

  4. Assign your proxy class for XMLHttpRequest to window.XMLHttpRequest

  5. Include this script before all other scripts

Now when any code runs new XMLHttpRequest() what they are getting is your XMLHttpRequestProxy object, which delegates to its internal connection pool. Even 3rd party libraries will be using this. Since the XMLHttpRequestProxy class has the same public interface as the real XMLHttpRequest class, the 3rd party libraries won't know the difference.

A basic stub of the JavaScript file would go something like:

(function (window) {
    var RawXMLHttpRequest = window.XMLHttpRequest;

    var requestPool = {
        // whatever you need to manage a pool of RawXMLHttpRequest objects

        getConnection: function () {
            // return a Promise
        }
    };

    function XMLHttpRequestProxy() {
        // Same properties and methods as real XMLHttpRequest

        var _xhr = null;

        function getConnection() {
            if (_xhr === null) {
                requestPool.getConnection()
                    .then(function (xhr) {
                        _xhr = xhr;

                        // return Promise, pass _xhr
                    });
            }

            // return Promise, pass _xhr
        }

        this.send = function(requestBody) {
            getConnection()
                .then(function (xhr) {
                    xhr.send(requestBody);
                });
        };
    }

    window.XMLHttpRequest = XMLHttpRequestProxy;
})(this);

While the code in the following link isn't the solution to your specific problem, it does illustrate this sort of pattern: MockingBird.XMLHttpRequestInterceptor(global)

You'll still have some juggling to do so the onreadystatechange handlers are called properly, and so the XMLHttpRequestProxy class properly tells the pool when an AJAX object is in use or freed up, but the basic template is there.

  • I admitted that pooling connections plus proxy for XMLHttpRequest will improve the overall response time (because the third party Map API did fire AJAX requests on its own when performing geoprocessing task and they will under control now). I will try it out to see If it also intercept other request from the Map API, so I could set the right priority to these request. – Incognitoo Oct 2 '18 at 2:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.