8

I have a long running service call, that I call using jQuery.ajax. The service can take well over 2 minutes to complete.

The AJAX request is submitted, and no response is expected. A separate AJAX request reports on the progress of the operation.

On some sites, we have found is that after 2 minutes the agent resubmits the Ajax request itself. The browser is Chrome, but I doubt its a Chrome related issue.

This definitely is not a case of us resubmitting the ajax request. To be sure we set a bool to prevent any re-submission in the beforeSend event.

The way I am handling now handling this re-submission, is by adding a nonce to the data request and the service tests if the nonce has already been submitted prior to carrying out its operation. Any second call to this service harmlessly returns and the original request continues to progress.

Note that I added a pointless service that does nothing but waiting for 5 minutes, and not once have I experienced the problem with the test service (on the production sites).

Can anyone give me any clues to what is causing this ajax re-submission, and how to go about reproducing it locally?

This is the js used to send the request:

var sent = false;
var data = { ... }; // data is a very large object;

$.ajax("service.ashx?loc=area/subarea/", {
    type: "POST",
    data: data,
    traditional: true,
    error: function (jqXHR, textStatus, errorThrown) {
        if (jqXHR.status === 0) {
            // see http://stackoverflow.com/questions/3825581/does-an-http-status-code-of-0-have-any-meaning

        } else {
            ReportFailure(textStatus,errorThrown);
        }
    },
    beforeSend: function () {
        if (sent === true) {
            return false;
        }
        sent = true;
        return true;
    }
});

Here is the ajax requested from a HTTP archive (HAR) where the request was re-submitted and it instantly failed. Notice the 2 minute time.

{
"startedDateTime": "2015-12-11T12:26:58.018Z",
"time": 120066.61499999973,
"request": {
  "method": "POST",
  "url": "https://example.com/service.ashx?loc=area/subarea/",
  "httpVersion": "HTTP/1.1",
  "headers": [
    ... // removed for brevity
  ],
  "queryString": [
    { "name": "loc", "value": "area/subarea/" }
  ],
  "cookies": [
    ... // removed for brevity
  ],
  "headersSize": 1163,
  "bodySize": 48048,
  "postData": {
    "mimeType": "application/x-www-form-urlencoded; charset=UTF-8",
    "text": ..., // removed for brevity
    "params": [
      ... // removed for brevity        ] }
},
"response": {
  "status": 500,
  "statusText": "Internal Server Error",
  "httpVersion": "HTTP/1.1",
  "headers": [
    {
      "name": "Date",
      "value": "Fri, 11 Dec 2015 12:28:57 GMT"
    },
    ... // removed for brevity
  ],
  "cookies": [],
  "content": {
  "size": 54,
  "mimeType": "text/xml",
  "compression": 0
  },
  "redirectURL": "",
  "headersSize": 305,
  "bodySize": 54,
  "_transferSize": 359
},
"cache": {},
"timings": {
  "blocked": 120005.055999998,
  "dns": -1,
  "connect": -1,
  "send": 1.0939999989932403,
  "wait": 59.986000001008506,
  "receive": 0.47900000172376167,
  "ssl": -1
},
"connection": "7498"
Drew Williams
  • 598
  • 4
  • 13
  • Did you check whether your code(ajax code) is actually getting called when the resubmit is happening... you can add a `console.log()` stmt before the ajax request and in the `beforeSend` handlers to check this - if your code is really sending the second request then the messages will get logged twice in the console – Arun P Johny Dec 14 '15 at 11:27
  • the next step could to either put a break point in the ajax line to see whether it is getting called twice and if see the stack trace to see how/where the second call is initiated from... or you can use the `console.trace()` logging to see the call stack – Arun P Johny Dec 14 '15 at 11:28
  • I'm certain its not my ajax request being resubmitted in code. For one the re-submission doesn't appear as a separate request in Chromes network profiler - which it would if the code was simply re-executed. beforeSend isn't executed a 2nd time. – Drew Williams Dec 14 '15 at 11:57
  • yes.. you are right... in that case I don't think it is the client side code that is causing it.... you will have to relook at your server side code... – Arun P Johny Dec 14 '15 at 11:59
  • I'm not sure what the problem in the server would be. The original request continues to execute even when the user agent has resubmitted the request. I'm trying to pin point why the request is resubmitted. – Drew Williams Dec 18 '15 at 09:35
  • I'm facing a similar problem today. were you able to resolve this issue when you faced it? Thanks. – chaudharyp Dec 01 '16 at 13:07
  • @DrewWilliams I'm also experiencing the same issue, and unable to address it. Did you get to the bottom of it? I'm using WebApi2 in Asp.net forms web app. – hynsey Mar 06 '20 at 10:04
  • Hi all. one thing to consider, which I've discovered was the cause of our issue. Our web servers are a pair behind a load balancer. And we were calling from ajax via the LB url. When we called direct to a server node it worked fine. So the LB is causing this issue for some reason. Will dig deeper into that for now, but should be enough to help you workaround it, or involve your LB support teams with WireShark traces to find out why it's doing that, and have them change it. – hynsey Mar 06 '20 at 10:43

4 Answers4

4

Same story here. jQuery obviously resends a request after 120 seconds (the default timeout) if no response has been received by the client so far.

Increasing the timeout as in:

    $.ajax({
      url: localhost,
      type: 'POST',
      timeout: 1000*60*10

doesn't change the game. Request still resends after 2 minutes, although the client continues to wait for any response.

although the server side is able to handle these two requests in parallel nicely, the clients request failes on valid response of the first request.

no solution yet

EDIT:

it actualy turned out, that the browser is the one, that resends the event and that behavior is fully compliant with HTTP/1.1 specs. And actualy the (node.js) backend closed the socket after a timeout of 120 seconds, such as jonasnas pointed to. The solution was to disable timeout at the response object, as in:

    // this is a long running request,
    // so disable response timeout
    response.setTimeout(0);
0

If request is handled always in 2min its an indication that your server side (node.js) is not finishing the response properly (with rsp.end()). 2 minutes is the default timeout on the express/node server.

jonasnas
  • 3,540
  • 1
  • 23
  • 32
  • The back end is a .net ASHX. I've experimented with this and don't believe it to be a server timeout. The ASHX has a timeout set to hours and the thread continues to execute even when the re-submission has completed or failed. As for a jQuery timeout, there is no global timeout in effect and no timeout specified for this instance either. – Drew Williams Dec 14 '15 at 11:53
0

Unfortunately, all other (browser-side) recommendations here did not work out for me. However, simply letting the ajax request timeout immediately does the job:

This can be achieved with:

$.ajax("https://someurl/", {
  ...
  timeout:1
  ...
});

As as side note: Setting timeout to 0 does not work.

If the server side computation takes so long, it does not make sense to wait here anyway. A separate solution is required to check the computation for completion.

This approach looks awful. So, any better working solutions are still highly welcome ;-)

0

Our issue was a load balancer. When we called the web api service from AJAX via the LB url, we get the 120 second issue, and repetetive calls to the service, with no representative extra calls from the client. When we call the api via a direct server node, removing the load balancer from the equation, we don't suffer the issue. If a load-balancer is in your equation, investigate if calling direct to server node works. If so, engage load balancer support.

As an interim workaround, until LB issue is solved, I will return the server node name to the client page on page load in a hidden field, and have my JS code use this to construct a direct URL for calling the service.

cigien
  • 57,834
  • 11
  • 73
  • 112
hynsey
  • 135
  • 1
  • 6