undo
Go Beyond the Code
arrow_forward_ios

How Self-Healing Long Polling Connector in Java Works

Emanuel Gialleonardo Indart
Software Engineer & Solver
November 5, 2026
To learn more about this topic, click here.

Applications constantly need to know when something changed on another system, and they need to know it fast. WebSockets solve this but add infrastructure overhead. Regular polling solves it too, but floods the server with empty responses. There is a third option that runs over plain HTTP and still delivers near-real-time updates. This tech note covers how it works, when it makes sense, and how we implemented it for a Salesforce integration using CometD and the Bayeux protocol.
‍

What is a Long polling connection?


‍‍Applications often need to receive updates from a server as soon as something changes. It can be a frontend application waiting for something to happen, or a service waiting for another service to complete a task. For this purpose, there are many mechanisms, and one of them is Long polling. You could use regular polling, sending requests until something changes, sending a request every 3 or more seconds, and having a lot of empty responses. Alternatively, you could set up Websocket; for simple things, it's a bit overkill.

When none of the commonly used mechanisms seem to fit, there is another option simpler than WebSockets that could help avoid sending dozens of unnecessary requests: long polling.

Long polling inverts the normal polling equation; instead of the client asking repeatedly and getting empty answers, the client sends a single HTTP request, and the server holds that request open until one of two things happens:

When new data becomes available, the server sends it back immediately. Or, a timeout is reached, in which the server sends an empty response. Either way, the moment the client receives the response (empty or not), it immediately opens a new request. This creates a continuous loop that looks like this:

Client connects to server, server holds connections open, waiting for new data to return. If new data arrives, a response is sent with the data. Client reconnects to the server, server holds connections open. If a timeout is reached (typically a higher timeout than normal), an empty response is returned; the client receives it and automatically reconnects to the server. These loops repeat forever, or as long as the client needs to retrieve updates from the server it wants to communicate with.

This mechanism sounds tricky and unnecessary in today’s world, but in special cases where this type of update is required between services or across different servers, it is useful. In the past, it has been used to communicate between frontend apps and the backend. Still, for those cases websocket has been a much better option for a long time now, so based on this for now and on, when i mention a “client” don’t think of it as a frontend app, we will focus on server to server communication, the client will be always a server.

But back at Long Polling Connections, the key here is that the connections are always open from the client’s perspective, the server responds the instant something happens, giving near real-time delivery, and the HTTP overhead only occurs when there is actually something to respond (or on timeout) instead of constantly sending request like in normal polling.
‍

Why does it work well?

Long polling runs on plain HTTP: no protocol changes, no special server support, no Websocket framing or infrastructure. Every HTTP-capable proxy, firewall, CDN, or load balancer in the world can handle it out of the box. This is not a small detail.

It maps naturally to request/response infrastructure, standard authentication headers, standard status codes,e and standard error handling. 

There is no need to manage persistent TCP connections, implement ping/pong heartbeats, handle frame fragmentation, or deal with half-open socket states. If your system supports an HTTP connection to another service, it supports long polling connections.

So.. Is it all happiness in the world of long polling connections? NO.

When we mention that we only use it for server-to-server communications, it is for multiple reasons. The days of long polling connections on web apps are long gone. Gmail used it in the beginning of its days, a lot of chat applications too, but there are several cons to its usage that make it a bad choice in front-end apps for server communication:

When is Long polling a good option: When server-to-server communication needs to be done in real time, and not through a queue mechanism, or when third-party APIs dictate it (CometD-based services).

When you need real-time delivery but your infrastructure doesn’t support websockets.

When you are working on corporate systems that use firewalls or proxies that block non-HTTP protocols.

When you want the reliability of HTTP (standard auth headers, standard logging, and status codes).

Or when your update frequency is moderate, talking about x events per second or minutes instead of high demand frequency (x events per millisecond).
‍

Bayeux protocol and CometD

CometD is a library implemented by Apache; it is the Apache CometD Library. It uses the Bayeux protocol to establish Long polling connections. But what is the Bayeux protocol?

Bayeux is a protocol specification for transporting asynchronous messages between a client and a server. It was designed specifically for the publish/subscribe pattern over HTTP. The protocol defines:


How CommetD implements it:

CommetD is the reference implementation of the Bayeux protocol. It includes a class called BayeuxClient that manages the entire long pollen lifecycle:

Handshake (/meta/handshake): The client sends its capabilities (supported transport, extensions, etc, etc). The server responds with session configuration and negotiated capabilities. This is a one-time setup step.

Connect (/meta/connect): After the handshake succeeds, the client enters the long polling loop. It sends a /meta/connect request. The server holds this request open. When data is available (or a timeout occurs), the server responds. The client processes the response and immediately sends a new /meta/connect request. This is the core long pollen mechanism. The CommetD library handles this loop automatically. You don’t need to implement the reconnection logic yourself.

Subscribe (/meta/subscribe): The client requests a subscription to a named channel/topic. The server confirms and begins routing messages from that channel to this client. There is also an unsubscribe request to stop this.


Extensions:

CommetD supports extensions that can intercept and modify messages at both the client and the server side. For example, in Ensolvers, we use CommetD for the Salesforce integrations in one of our projects. We have developed an extension called ReplayExtension on the client side of the long polling connection. This extension is in charge of automatically adding information required by Salesforce to the requests the connector sends to establish the long polling connection. This information includes things like the ID of the last events read by our system for each of the topics we subscribe to in the connection. It also intercepts incoming messages to extract and save that same ID for new events.
‍

Details of our implementation that could be interesting.

For now and on, we will talk about one of our current long polling implementations and its details, to explain and show what things need to be considered when trying to use long polling with CommedD and bayeux protocol. As we mentioned before in the extensions section, we have a long polling connection implemented for a Salesforce integration, which is in charge of retrieving information for the integrated users into our system.
‍

Replay Mechanism. 

What is a replay mechanism? It is a system that is in charge of keeping control of what was processed and what was not. In this long polling connector, the replay mechanism is in charge of keeping track of the event IDs that we have read from Salesforce. For this purpose, we have the ReplayExtension mentioned before attached to outgoing and incoming messages of the connector.

But why is it really needed if we said before that long polling connections are great for real-time updates… 

If the connection drops for a few seconds, events may occur during that time; without protection, those events would be lost forever. The connector stores the last event ID (replay ID) for each topic. When reconnecting, it tells Salesforce, “Send me all events after this ID.” Salesforce then resumes delivery exactly where the connector left off.

‍
Subscriptions (Topics)

Subscriptions define what events we want to receive from Salesforce, for example, lead updates, opportunity changes, custom push topics, sales, etc. Each subscription represents one event stream. 

Each subscription has the topic name, last replay position (event ID), and a consumer (the function in charge of handling the processing of those events).

Subscriptions are useful because they allow the connector to manage multiple event streams simultaneously. They automatically re-subscribe after reconnecting from the long polling loop, and you can unsubscribe at any point to stop processing those events.

The most important thing here is the isolation of the processing of each topic; they are 100% independent, thanks to the consumer mechanism that you can add to each one of them.


Connection monitoring and recovery

Real networks are unreliable. The connector we have built includes several mechanisms to detect and recover from failure.


Health Checks (Anomaly validator)

Once the long pollen connection has been established, it will loop infinitely, receiving information. But what happens if something external to the connector changes, and it needs to act based on that? Examples: The integration was disabled. A topic was removed, and it should be unsubscribed from.

We have multiple mechanisms implemented in the connector to handle that:

These mechanisms are important;t, without these checks, the connector could run forever for deleted integrations or keep failing on invalid topics.


Service layer integration (our business logic connected to the Long polling connector)‍

It is important to keep the long polling implementation separated from the business implementations. The connector should be able to be used in Salesforce and any other system that requires this type of connection (as long as it uses the same Bayeux protocol, of course). 

For this reason, the connector uses a lot of functional programming to delegate actions to the Service that is trying to use them.

The service will create the connector and give him a configuration, but it will require the implementation of his own relay ID storing mechanism, his own OAuth token refreshing mechanism, integration status update, deduplication events, etc. Those implementations will be called by the connector, but the implementation will live in the service, in our case, the SalesforceIntegrationService. 

Why is it important? It creates a clear architecture:

This makes the connector reusable by other integrations.


How Everything works together (how this long polling connector finally works!)‍

We will enumerate a list of steps this system follows from 0 to having the long polling connector up and running. Here we go:


If connections break:

if authentication expires:

Final conclusions

So. this is how our implementation of Long polling connector works for Salesforce integration, i hope it helps understand the things that need to be considered at the moment of using this type of communication, we have go through what a Long polling connection is, what are the use cases that can be good for it on these days, its pros and cons and what to be careful about when using it (principally the thread occupation.)

It is a mechanism of communication of the past, that's for sure, used today for more specific purposes than before; the general use of it has been replaced by more up-to-date mechanisms, principally WebSockets for web applications communicating bidirectionally with a server.

The use of this communication method has been reduced to third-party integrations and internal server communication on environments where HTTP is the ideal protocol.

When the implementation of this mechanism considers all the possible failure points that come with keeping open HTTP connections and reconnecting in a loop to them, it can become a strong way to keep systems up to date with real-time updates of each other.

But its strength and reliability are strictly related to how well the pushing and reading systems are; it puts a lot of the configuration and mechanism that guarantees its working on how it was developed. It's not a framework that works out of the box.

This was a short tech note on what a long polling connection is, how it works, what it can be used for, and a small example of how we use it and how we implemented it on an integration one of our systems uses.

Emanuel Gialleonardo Indart
Software Engineer & Solver
Arrow icon go to top

Start Your Digital Journey Now!

Which capabilities are you interested in?
You may select more than one.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.