LP1912834 OpenSRF JS Max Parallel Requests user/berick/lp1912834-max-parallel-net-v2
authorBill Erickson <berickxx@gmail.com>
Fri, 22 Jan 2021 20:53:40 +0000 (15:53 -0500)
committerBill Erickson <berickxx@gmail.com>
Mon, 25 Jan 2021 21:27:44 +0000 (16:27 -0500)
Limit the number of active network requests coming from OpenSRF
(WebSocket, XHR) to avoid flooding servers with excessive numbers of
active requests.

The value is currently hardcoded to 5 in opensrf.js.

When the max number of requests limit is reached and new requests
continue to arrive, a warning message is logged to notify the developer
to consider refactoring the client code to use batched calls / API's.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
src/javascript/opensrf.js

index 03c79a9..4e138f5 100644 (file)
@@ -64,6 +64,8 @@ var OSRF_STATUS_VERSIONNOTSUPPORTED = 505;
 // TODO: get path from ./configure prefix
 var SHARED_WORKER_LIB = '/js/dojo/opensrf/opensrf_ws_shared.js'; 
 
+var MAX_PARALLEL_REQUESTS = 5;
+
 /* The following classes map directly to network-serializable opensrf objects */
 
 function osrfMessage(hash) {
@@ -272,6 +274,57 @@ OpenSRF.set_subclass = function(cls, pcls) {
 };
 
 
+// Some static request queueing functions.
+OpenSRF.pendingRequests = [];
+OpenSRF.throttleAlerted = false;
+OpenSRF.activeCount = 0;
+
+OpenSRF.sendFromQueue = function() {
+    var pending = OpenSRF.pendingRequests;
+
+    while (pending.length > 0 && OpenSRF.activeCount < MAX_PARALLEL_REQUESTS) {
+        var request = pending.shift();
+
+        var ses = request.session;
+        var osrf_msg = request.osrf_msg;
+        var req_args = request.req_args;
+
+        if (osrf_msg.type() != 'DISCONNECT') {
+            // DISCONNECT messages do not return a response, so there's
+            // no way to know when have completed.  Avoid tracking them
+            // as 'active'
+            OpenSRF.activeCount++;
+        }
+
+        ses.sendToNet(osrf_msg, req_args);
+    }
+
+    if (pending.length > 0) {
+        if (!OpenSRF.throttleAlerted) {
+
+            OpenSRF.throttleAlerted = true;
+            // Seeing this log in the console means the coder
+            // should consider refactoring to use batch calls.
+            console.warn('Net throttling activated on max requests');
+        }
+
+    } else {
+        // Reset now the backlog has been cleared.
+        OpenSRF.throttleAlerted = false;
+    }
+
+    // Useful for debugging issues with pending/active queues
+    //console.log('pending: ' + pending.length + ' active: ' + OpenSRF.activeCount);
+}
+
+OpenSRF.removeFromQueue = function() {
+    OpenSRF.activeCount--;
+
+    // Every completed request is a chance for a pending request
+    // to make it to the big leagues.
+    OpenSRF.sendFromQueue();
+}
+
 /* general session superclass */
 OpenSRF.Session = function() {
     this.remote_id = null;
@@ -289,6 +342,15 @@ OpenSRF.Session.prototype.cleanup = function() {
 };
 
 OpenSRF.Session.prototype.send = function(osrf_msg, args) {
+    OpenSRF.pendingRequests.push({
+        session: this,
+        osrf_msg: osrf_msg,
+        req_args: args
+    });
+    OpenSRF.sendFromQueue();
+}
+
+OpenSRF.Session.prototype.sendToNet = function(osrf_msg, args) {
     args = (args) ? args : {};
     switch(OpenSRF.Session.transport) {
         case OSRF_TRANSPORT_TYPE_WS:
@@ -706,6 +768,7 @@ OpenSRF.Stack.handle_message = function(ses, osrf_msg) {
 
 
         if(status == OSRF_STATUS_COMPLETE) {
+            OpenSRF.removeFromQueue(ses, osrf_msg);
             if(req) {
                 req.complete = true;
                 if(req.oncomplete && !req.oncomplete_called) {
@@ -716,6 +779,7 @@ OpenSRF.Stack.handle_message = function(ses, osrf_msg) {
         }
 
         if(status == OSRF_STATUS_OK) {
+            OpenSRF.removeFromQueue(ses, osrf_msg);
             ses.state = OSRF_APP_SESSION_CONNECTED;
 
             /* call the connect callback */
@@ -727,6 +791,7 @@ OpenSRF.Stack.handle_message = function(ses, osrf_msg) {
 
         // capture all 400's and 500's as method errors
         if ((status+'').match(/^4/) || (status+'').match(/^5/)) {
+            OpenSRF.removeFromQueue(ses, osrf_msg);
             if(req && req.onmethoderror) 
                 return req.onmethoderror(req, status, status_text);
         }