62

I'm trying to implement long polling for the first time, and I'm using XMLHttpRequest objects to do it. So far, I've been successful at getting events in Firefox and Internet Explorer 11, but Chrome strangely is the odd one out this time.

I can load one page and it runs just fine. It makes the request right away and starts processing and displaying events. If I open the page in a second tab, one of the pages starts seeing delays in receiving events. In the dev tools window, I see multiple requests with this kind of timing:

screenshot

"Stalled" will range up to 20 seconds. It won't happen on every request, but will usually happen on several requests in a row, and in one tab.

At first I thought this was an issue with my server, but then I opened two IE tabs and two Firefox tabs, and they all connect and receive the same events without stalling. Only Chrome is having this kind of trouble.

I figure this is likely an issue with the way in which I'm making or serving up the request. For reference, the request headers look like this:

Connection: keep-alive
Last-Event-Id: 530
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Accept: */*
DNT: 1
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

The response looks like this:

HTTP/1.1 200 OK
Cache-Control: no-cache
Transfer-Encoding: chunked
Content-Type: text/event-stream
Expires: Tue, 16 Dec 2014 21:00:40 GMT
Server: Microsoft-HTTPAPI/2.0
Date: Tue, 16 Dec 2014 21:00:40 GMT
Connection: close

In spite of the headers involved, I'm not using the browser's native EventSource, but rather a polyfill that lets me set additional headers. The polyfill is using XMLHttpRequest under the covers, but it seems to me that no matter how the request is being made, it shouldn't stall for 20 seconds.

What might be causing Chrome to stall like this?

Edit: Chrome's chrome://net-internals/#events page shows that there's a timeout error involved:

t=33627 [st=    5]      HTTP_CACHE_ADD_TO_ENTRY  [dt=20001]
                --> net_error = -409 (ERR_CACHE_LOCK_TIMEOUT)

The error message refers to a patch added to Chrome six months ago (https://codereview.chromium.org/345643003), which implements a 20-second timeout when the same resource is requested multiple times. In fact, one of the bugs the patch tries to fix (bug number 46104) refers to a similar situation, and the patch is meant to reduce the time spent waiting.

It's possible the answer (or workaround) here is just to make the requests look different, although perhaps Chrome could respect the "no-cache" header I'm setting.

Quuxplusone
  • 23,928
  • 8
  • 94
  • 159
mrdecemberist
  • 2,631
  • 2
  • 20
  • 23

4 Answers4

54

Yes, this behavior is due to Chrome locking the cache and waiting to see the result of one request before requesting the same resource again. The answer is to find a way to make the requests unique. I added a random number to the query string, and everything is working now.

For future reference, this was Chrome 39.0.2171.95.

Edit: Since this answer, I've come to understand that "Cache-Control: no-cache" doesn't do what I thought it does. Despite its name, responses with this header can be cached. I haven't tried, but I wonder if using "Cache-Control: no-store", which does prevent caching, would fix the issue.

mrdecemberist
  • 2,631
  • 2
  • 20
  • 23
  • 1
    Does that mean that if you repeatedly request your site by spamming [F5] on your keyboard Chrome will visually "hang" on loading? – Tomasz Kowalczyk Mar 30 '15 at 20:30
  • I don't think so. More like if you loaded the same page in multiple tabs at the same time. The cache hang happens when making multiple requests to the same URL at the same time. In my case, these were long-poll responses, so on the second request, Chrome would wait for the first request to complete to see if it could reuse the response. The first request would take longer than 20 seconds to finish, and Chrome would wait that long before giving up. – mrdecemberist Mar 31 '15 at 20:59
  • Could you please check website http://hash.fm and test it a little bit? I'm currently handling such bug report and what's interesting latest Firefox has no problem with that, but Chrome (41.0.2272.101) stalls requests after several (different every time) refreshes. When I repeatedly hit [F5] website of course loads a little bit slower but with Firefox I see everything in Apache access logs, Chrome does not even send a request. Thanks in advance. – Tomasz Kowalczyk Apr 01 '15 at 07:45
  • When looking at your site, I don't see much time spent in "stalled." It doesn't appear to be the same phenomenon. I do see one major thing making your site slow; a lot of responses are coming back with the "Connection: close" header, which kills streamlining and makes the site very dependent on the number of connections the browser can make to the site. This may cause what you're seeing. Also, at this moment, the server has stopped serving a lot of the resources to me; I'm seeing connections just timeout. Get streamlining working and then try again. – mrdecemberist Apr 02 '15 at 15:11
  • Strike the comment about the server not sending me resources; my Chrome tab apparently got stuck after so many reloads. I've also disabled an extension that was making the connections appear worse than they were. I'm definitely not seeing any cache issues, because your site hardly uses the cache. Biggest issue is still Connection: close. I'm seeing a stall time of 18ms for the very first resources because they can't reuse the initial page connection. They have to wait for a new one because the initial page closes its connection. – mrdecemberist Apr 02 '15 at 15:27
  • I do see one other interesting thing: on refresh, Chrome says that it's sending the headers and not getting a response for awhile. See my capture at http://pastebin.com/K46kH7pi. In that capture there's a two second delay between sending the request and receiving the reply headers. That would be the TTFB stat in Chrome's network timing screen. Perhaps your server is getting overwhelmed servicing all those connections? – mrdecemberist Apr 02 '15 at 15:36
  • Adding a random query string unfortunately didn't work for me; any other suggestions on how to make the request appear unique? – Brad Dwyer Apr 09 '15 at 20:16
  • If you have two requests with different URLs, they should not be blocking each other in the cache. Are you seeing 20 seconds of stalling? – mrdecemberist Apr 10 '15 at 21:12
  • Hey guys, did you ever solve this? I'm experiencing a similar thing. I have a plugin that makes API on every page load via ajax, but chrome hangs for a bit then it loads it fine for like 30 requests, then it will hang again. While it is hanging. I can open up safari, make the same request and it loads just fine. I paste the same url in chrome, it hangs. – Mark Evans Aug 07 '15 at 07:54
  • Here is what i'm seeing http://pastebin.com/C2KWqgis Notice the delays around: t=64063 [st=66671] HTTP_STREAM_PARSER_READ_HEADERS [dt=72] t=64135 [st=66743] HTTP_TRANSACTION_READ_RESPONSE_HEADERS – Mark Evans Aug 07 '15 at 08:01
  • I'm on Chrome for Mac 44.0.2403.130 – Mark Evans Aug 07 '15 at 08:19
  • You're likely also seeing something else. If you were hitting the cache lock, it would have timed out after 20 seconds. You're seeing delays of 60 seconds. The pastebin you sent doesn't show where the time is being spent (look for the line where dt is large), only that it happened before the HTTP_TRANSACTION_SEND_REQUEST completed. – mrdecemberist Aug 10 '15 at 14:42
  • 4
    `Cache-Control: no-store` worked for me! Very helpful nice answer, thank you :D – mpyw Aug 30 '16 at 15:26
  • `Cache-Control: no-store` didn't work well for me somehow while adding a random number worked like a charm – Mike Jan 17 '17 at 09:57
  • 3
    I was having this issue and `Cache-Control: no-cache, no-store` worked for me. Big thanks! – Michael Tiller Feb 03 '17 at 20:33
  • 3
    I deliberately put in a 10 second "sleep" in the server and found that if I refresh 2 chrome windows with the same url at the same time, the first one takes 10 seconds and the second takes 20. I can see from the server logs that chrome doesn't send the second request until the first has completed, though it shows the status as "pending" in the second window for the whole 20 seconds. This is regardless of HTTP headers. This doesn't happen if the URLs are different or with firefox. – Andy Feb 18 '21 at 11:16
  • Unluckily Cache-Control: no-store || Cache-Control: no-cache, no-store won't launch multiple requests. With FF it's ok!, Chrome ver.: 89.0.4389.90 – bertasoft Mar 29 '21 at 13:31
  • In case it wasn't obvious, doing `Cache-Control: no-cache, no-store` will prevent chrome from caching the response, which seems fine for the OP, but might degrade other use cases. Also note that each new URL could trigger an additional OPTIONS request for CORS roundtrip if you're setting non-simple headers (e.g. Range). https://bugs.chromium.org/p/chromium/issues/detail?id=969828 seems to be about making this faster – mkirk Jun 15 '21 at 21:13
  • accepted answer is not actually working anymore. adding cache-control does not allow parallel requests in newer chrome version (I checked at version 100) – Sly Apr 12 '22 at 11:50
  • After trying all those above solutions i didnt find the solution. So i opened two tabs with both another PHP page on the same domain. The first one had a sleep(10), the other direct PHP processing. The apache log told me both were sent directly, but the writing order of the log was mixed. So Chrome didnt cause the delay. It turned out PHP was creating the delay. https://serverfault.com/a/194449/454355 helped me: session_start() creates a session file LOCK, which blocks all other requests until the current page load completes. By calling session_write_close() the lock is removed. Then it works! – Tim B. Jun 21 '22 at 11:59
2

adding Cache-Control: no-cache, no-transform worked for me

activedecay
  • 10,129
  • 5
  • 47
  • 71
2

I have decided to keep it simple and checked the response headers of a website that did not have this issue and I changed my response headers to match theirs:

Cache-Control: max-age=3, must-revalidate
Corion
  • 3,855
  • 1
  • 17
  • 27
DevDan
  • 21
  • 4
0

I ran into the same issue. Cache-Control header didn't help. But I just added extra query parameter with random value and made all requests unique. And it worked.

GET /long-polling?rand=123

GET /long-polling?rand=456

Chrome considered them as different requests.

Vlad Ganshin
  • 115
  • 5