Please be aware that you are viewing our bleeding edge unstable documentation. Unless you wanted to view the bleeding edge (and possibly unstable) documentation, we recommend you use our stable docs.

Go to Ably's stable canonical documentation »

I know what I'm doing, let me see the bleeding edge docs »

You are viewing our bleeding edge unstable documentation. We recommend you use our stable documentation »
Fork me on GitHub

Long Polling - Concepts and Considerations

In this article, we’ll provide an insight into the technique known as “long polling”, how it came to be, how it is implemented, how you might use it in your own applications, and how we use it at Ably.

A brief history – How did long polling come about?

In the early days of the web, HTTP made sense as a simple request-response protocol because it was designed as a way to distribute structured documents to interested readers, each document generally having links to others that could be browsed at the reader’s leisure. A request-response model was the right strategy because a browser needed only make a request for the resource that the user wished to consume, and the job was done. The technology of the time was vastly more limited than that of today, and holding a connection open would not only have been a significant issue for web server load management but would have had little-to-no value anyway, concerning the goals and use cases that the early web was designed to solve. Early browsers such as Mosaic and the first version of Netscape Navigator were implemented with this in mind and had no support for JavaScript or other features that might position the web as anything more than a distributed resource-sharing network.

In 1995, Netscape Communications hired Brendan Eich to implement scripting capabilities in Netscape Navigator, and over a ten-day period, the JavaScript language was born. Its capabilities as a language were initially very limited compared to modern-day JavaScript, and its ability to interact with the browser’s document object model (DOM) was even more limited. JavaScript was mostly useful for providing limited enhancements to enrich document consumption capabilities, examples being in-browser form validation and lightweight insertion of dynamic HTML into an existing document.

As the browser wars heated up, with Microsoft’s Internet Explorer reaching version 4 and beyond, the battle for the most robust feature set led to Microsoft’s introduction of what ultimately became the XMLHttpRequest object which all browsers have now universally supported for well over a decade.


Browser Wars

XMLHttpRequest could be thought of as a black swan event for the web, because it opened up the potential for web developers to start building truly-dynamic web applications that could communicate with the server silently in the background, without interrupting the user’s browsing experience. Because browser DOM capabilities had also been expanding significantly, particularly as Internet Explorer approached version 6, dynamic web “applications” started to become commonplace, offering everything from rich web-based email interfaces to in-browser website content management systems.

Progress begets progress, as they say, and naturally, developers began to explore ways to implement applications having more of a “real-time” aspect to their function. Web-based chat rooms and simple games were early examples at the time. The HTTP protocol made these sorts of use cases very challenging to implement though, and it was common to see applications polling servers repeatedly to check for new data, seeing as servers had no way to proactively notify a user whenever new data became available. Given the extreme inefficiency of this approach, creative ways to manipulate the HTTP request-response model into more of a real-time medium began to emerge. Among these techniques, probably the most popular to emerge was “long polling”.

How does long polling work?

Long polling is essentially a more efficient form of the original polling technique. Making repeated requests to a server wastes resources, as each new incoming connection must be established, the HTTP headers must be parsed, a query for new data must be performed, and a response (usually with no new data to offer) must be generated and delivered. The connection must then be closed, and any resources cleaned up. Rather than having to repeat this process multiple times for every client until new data for a given client becomes available, long polling is a technique where the server elects to hold a client’s connection open for as long as possible, delivering a response only after data becomes available or a timeout threshold has been reached.

Implementation is mostly a server-side concern. On the client side, only a single request to the server needs to be managed. When the response is received, the client can initiate a new request, repeating this process as many times as is necessary. The only difference to basic polling, as far as the client is concerned, is that a client performing basic polling may deliberately leave a small time window between each request so as to reduce its load on the server, and it may respond to timeouts with different assumptions than it would for a server that does not support long polling. With long polling, the client may be configured to allow for a longer timeout period (via a Keep-Alive header) when listening for a response – something that would usually be avoided seeing as the timeout period is generally used to indicate problems communicating with the server.


Long Polling

Apart from these concerns, there is little else that a client needs to do that would be different than if it were engaging in basic polling. By contrast, the server needs to manage the unresolved state of multiple connections, and it may need to implement strategies for preserving session state when multiple servers and load balancers are in use (commonly referred to as session “stickiness”). It also needs to gracefully handle connection timeout issues, which are much more likely to occur than with designed-for-purpose protocols such as WebSockets, a standard which did not arrive until years after long polling was established as a conventional technique for pseudo-real-time communication.

Considerations when using long polling

As long polling is really just an improvisation applied to an underlying request-response mechanism, it comes with an additional degree of complexity in its implementation. There are various concerns you’ll need to account for when considering the design of your system’s architecture.

Message ordering and delivery guarantees

Reliable message ordering can be an issue with long polling because it is possible for multiple HTTP requests from the same client to be in flight simultaneously. For example, if a client has two browser tabs open consuming the same server resource, and the client-side application is persisting data to a local store such as localStorage or IndexedDb, there is no in-built guarantee that duplicate data won’t be written more than once. This could also happen if the client implementation uses more than one connection at a time, whether deliberately or as a result of a bug in the code.

Another issue is that a server may send a response, but network or browser issues may prevent the message from being successfully received. Unless some sort of message receipt confirmation process is implemented, a subsequent call to the server may result in missed messages.

Depending on the server implementation, confirmation of message receipt by one client instance may also cause another client instance to never receive an expected message at all, as the server could mistakenly believe that the client has already received the data it is expecting.

All of these concerns, and more need to be considered when implementing robust support for long polling in any real-time messaging system.

Performance and scaling

Unfortunately, such complexity is difficult to scale effectively. To maintain the session state for a given client, that state must either be sharable among all servers behind a load balancer – a task with significant architectural complexity – or subsequent client requests within the same session must be routed to the same server to which their original request was processed. This form of deterministic “sticky” routing is problematic by design, especially when routing is performed on the basis of IP address, as it can place undue load on a single server in a cluster while leaving other servers mostly idle instead of spreading the load around efficiently. This can also become a potential denial-of-service attack vector – a problem which then requires further layers of infrastructure to mitigate that might otherwise have been unnecessary.

Device support and fallbacks

In modern times (2018 at the time of this article), long polling can be less relevant for web application development given the widespread availability of real-time communication standards such as WebSockets and WebRTC. That said, there are cases where proxies and routers on certain networks will block WebSocket and WebRTC connections, or where network connectivity can make long-lived connection protocols such as these less practical. Besides, for certain client demographics, there may still be numerous devices and clients in use that lack support for newer standards. For these, long polling can serve as a good fail-safe fallback to ensure support for everyone, irrespective of their situation.

Given that long polling is implemented on the back of XMLHttpRequest, which is near universal in its support by devices that still have a non-negligible degree of remaining relevant on the modern web, there’s usually not much of a need to support further fallback layers. In cases where exceptions must be handled though, or where a server can be queried for new data but does not support long polling (let alone other more modern technology standards), basic polling can sometimes still be of limited use, and can be implemented using XMLHttpRequest, or via JSONP through simple HTML script tags.

Long polling can be implemented via JSONP if absolutely necessary. That said, given the time and effort – not to mention the inefficiency of resource consumption – involved in implementing these approaches, care should be taken to assess whether their support is worth the added cost when developing new applications and system architectures. In many cases, you may be able to get away with exclusive support for something more modern, such as WebSockets. Alternatively, you may wish to offload the management of these sorts of concerns to a specialist cloud provider such as Ably Realtime.

Open source long polling solutions

Most libraries don’t implement long polling in isolation from other transports because, in general, long polling is usually accompanied with other transport strategies, either as a fallback or with those transports as fallbacks when long polling doesn’t work. In 2018 and beyond, standalone long polling libraries are particularly uncommon, given that it’s a technique that is quickly losing relevance in the face of widespread support for more modern alternatives.

Nevertheless, below are a handful of options for a few different languages:

Go: golongpoll

A decent library for implementing long polling in the Go language.

import	"github.com/jcuga/golongpoll"

// This launches a goroutine and creates channels for all the plumbing
manager, err := golongpoll.StartLongpoll(golongpoll.Options{})  // default options

// Pass the manager around or create closures and publish:
manager.Publish("subscription-category", "Some data.  Can be string or any obj convertible to JSON")
manager.Publish("different-category", "More data")

// Expose events to browsers
// See subsection on how to interact with the subscription handler
http.HandleFunc("/events", manager.SubscriptionHandler)
http.ListenAndServe("127.0.0.1:8081", nil)

You can find out more about this library from the original GitHub repo.

PHP: php-long-polling

As per the readme, php-long-polling is “a very simple demonstration of long-polling with AJAX (jQuery) and PHP … This is an improved, cleaned and documented fork of (php-ajax-long-polling)[https://github.com/lincolnbrito/php-ajax-long-polling]”.

As the code for this one is extremely simple you can take a look at the implementation directly – it’s fairly self-explanatory.

You can find out more about this library from the original GitHub repo.

Node.js: Pollymer

From the readme, "Pollymer is a general-purpose AJAX library that provides conveniences for long-polling applications, such as request retries, exponential backoff between requests, randomized request delaying, and workarounds for browser “busy” indications. It also implements multiple transports to ensure cross-domain access works in all major browsers."

Optional extras include support for JSON-P and logging.

var req = new Pollymer.Request();
req.on('finished', function(code, result, headers) { ... });
req.on('error', function(reason) { ... });
var headers = { ... };
var body = 'some data';
req.maxTries = 2; // try twice
req.start('POST', 'http://example.com/path', headers, body);

You can find out more about this library from the original GitHub repo.

Python: A simple COMET server

This is a minimalist HTTP long-polling server for the Python language. It supports multiple channels and cross-domain requests. It requires Python >= 2.5 and the Twisted framework. Unlike the other libraries mentioned above, this server runs as a kind of broker that sits between the client and the server-side applications. When the server wishes to publish messages to a client, it can make a request to an HTTP endpoint that can be easily hidden behind a firewall, ensuring that is only accessible by server-side processes.

The README for details provides examples for:

You can find out more about this library from the original GitHub repo.

Ably and long polling

At Ably Realtime we’ve implemented a robust suite of transports in the client libraries that we make available for communication with our servers, with WebSockets being the primary protocol for realtime communications. However, long polling is fully-supported as an automatic fallback transport when newer standards such as WebSockets are not available on the client-side. Find out more about the Ably Realtime platform.

References and further reading


Back to top