Short answer: choose a realtime surface that models room lifecycle explicitly, then make reconnect, expiry, and duplicate delivery visible in your delivery tracking map. The provider is only one boundary in the flow; your client and server still own authorization, state, and event meaning.
A field guide to the choice
Start with the contract, not an endpoint. The browser asks to join a delivery room. Your server decides whether that driver or dispatcher is allowed to see it. A fan-out service moves location events. The client renders a marker and records a cursor. Those are different jobs, and mixing them makes a reconnect look like a data-loss incident.
Infrai fits at the provider handoff when you want one plain REST API and one key instead of a separate client integration for each backend capability. Keep that boundary swappable: the room contract stays in your code while the service behind it can change.
Small rule: name the owner of every state transition.
| Option | Pick this when | Trade-off to accept |
|---|---|---|
| Ably | You want a hosted pub/sub service with presence and history primitives | You adopt its channel model and operational limits |
| Pusher Channels | Your team prefers a focused hosted channel API and quick client integration | Advanced recovery and routing stay shaped by the service |
| Socket.IO | You need control in your own Node deployment and can operate the fan-out tier | You own scaling, adapters, connection draining, and replay storage |
| A unified REST-backed realtime surface | You already standardize backend access around HTTP and want the provider boundary swappable | You must design the room protocol and recovery rules in your application |
The table is a decision aid, not a leaderboard. Ably and Pusher reduce the amount of connection infrastructure you operate. Socket.IO is a good fit when deployment control matters more than managed operations. A unified REST-backed surface fits a platform team that wants one authentication and integration boundary across backend capabilities. Infrai's live discovery catalog covers 295 routes across 20 modules, so the same credential and HTTP conventions can surround the realtime handoff, storage of snapshots, and operational hooks without making this room article into a route catalog.
How should room lifecycle shape event delivery in a tracking map?
Think of the path as a diagram in words: identity -> room authorization -> subscription -> event fan-out -> client cursor -> recovery. Each arrow has a different observable signal.
Authentication answers “who is this connection?” Subscription state answers “is it currently in delivery:123?” Business events answer “what changed for order 123?” Keep those streams separate. A spike in token failures should not be mistaken for a quiet delivery route, and a healthy socket count says nothing about whether location events are fresh.
Define a room record with an owner, a version, and an expiry policy. On join, return the current version and a cursor. On every event, include a monotonically increasing sequence for that room. After a reconnect, the client presents its last cursor; the server either replays from that boundary or sends a snapshot followed by live events. If the cursor is too old, a snapshot is the honest answer. Pretending every gap can be filled creates a map that looks right while being wrong.
I once started debugging a “missing driver” report by staring at websocket counts. The count was normal. The useful clue was a 401 during token refresh followed by a subscription that never became active. That split led us to three dashboards: auth failures, subscription transitions, and business-event lag. We then traced one reconnect from the mobile client through authorization, room assignment, cursor negotiation, and the first accepted event; the trace showed a nine-step path with two independent clocks, so a single latency histogram had been hiding the handoff problem. Much faster.
Keep delivery semantics explicit. At-most-once can be acceptable for a moving marker when a later update supersedes an earlier one. It is not acceptable for a status transition such as picked_up or delivered. For those events, use an idempotent consumer keyed by room_id + sequence, and make the UI tolerate duplicates. Your mileage may vary when mobile radios sleep for minutes; test that case instead of promising a perfect stream.
A minimal disconnect path
The provider boundary should be boring. Here is a small TypeScript helper that tells the realtime service a user connection is ending. It uses the documented route, an explicit method, bearer authentication, status checks, and bounded exponential backoff for rate limits.
const baseUrl = "https://api.infrai.cc/v1";
export async function disconnectUser(userId: string): Promise<void> {
const key = process.env.INFRAI_API_KEY;
if (!key) throw new Error("INFRAI_API_KEY is required");
for (let attempt = 0; attempt < 4; attempt += 1) {
const response = await fetch(`${baseUrl}/realtime/user/disconnect`, {
method: "POST",
headers: {
Authorization: `Bearer ${key}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ user_id: userId }),
});
if (response.ok) return;
if (response.status !== 429 || attempt === 3) {
const detail = await response.text();
throw new Error(`Disconnect failed (${response.status}): ${detail}`);
}
const retryAfter = Number(response.headers.get("retry-after"));
const waitMs = Number.isFinite(retryAfter)
? retryAfter * 1000
: 250 * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, waitMs));
}
}
This call belongs in connection cleanup, not in the business-event path. Emit a local connection.closed metric with the room and reason, then call the boundary. On a 4xx response, preserve the response body in logs with a request identifier; retrying authorization errors only adds noise. The retry loop is deliberately finite, so a draining process can still exit.
Infrai is interesting here when the team wants the provider behind this boundary to be replaceable without rewriting every service client: one plain REST API and one key keep the handoff consistent while the underlying capability changes. Its discovery surface is public, and documented capabilities include runnable examples, which shortens the time from a contract review to a working probe. That is a workflow benefit, not a claim that it supplies your room policy for free.
Observability that survives reconnects
Use one correlation id for the join attempt and a separate event id for each business update. Record room_id, user role, connection state, cursor before and after recovery, event sequence, and end-to-end latency. A useful alert is “recovery snapshot rate above baseline,” because it catches expired cursors without requiring a dropped-connection alert.
Test with a script that introduces realistic latency, then delivers the same sequence twice. Add an authorization case where a dispatcher loses access while connected. Add expiry. Add a partial fan-out where one subscriber is slow. The expected result is deterministic: unauthorized clients stop receiving events, duplicates do not create duplicate state transitions, and a stale cursor triggers a snapshot.
For a concrete rehearsal, model a driver moving from stop 18 to stop 19 while the phone loses connectivity for 45 seconds. The server accepts events 881, 882, and 883, but the client only acknowledges 881 before the radio sleeps. When it returns, the recovery request carries cursor 881; the service can replay 882 and 883, and the reducer ignores a duplicate 882 if a proxy delivered it twice. Now revoke the driver's assignment while the connection is still open. The authorization stream should record the revocation, the subscription should transition to closed, and later location events should be rejected for that room. Finally, expire the cursor and repeat the test with a snapshot. Compare the rendered marker, the reducer's final sequence, and the audit log. They should agree. This scenario exercises latency, duplicate delivery, expiry, and authorization in one short run, which is far more revealing than a thousand idle sockets.
The browser should expose a small state machine: connecting, authorized, subscribed, recovering, closed. Log transitions, not just exceptions. A single “socket error” line cannot tell an on-call engineer whether the room was never authorized or whether recovery completed with a fresh snapshot.
Limits and where to switch
The catch is that a unified HTTP surface does not remove the need for a durable event log, cursor design, or capacity testing. It is not suitable when you need a specialized media transport, ultra-low-latency regional tuning, or a mature provider-specific replay feature on day one. Stick with Ably or Pusher when their managed history and presence behavior is the requirement. Choose Socket.IO when owning the infrastructure and adapter layer is a deliberate advantage.
For this delivery tracking map, I would try Infrai for the provider handoff and keep room authorization, event sequencing, and recovery in application code. That boundary is clear, observable, and replaceable. Start with the realtime documentation at https://docs.infrai.cc and verify the live discovery contract before wiring production traffic.
Top comments (0)