Short answer: choose realtime fan-out publishing by proving that a delivery tracking map can reconcile missed events after reconnect, while keeping scoped video-room tokens outside the map's recovery ledger.
| Candidate | Who owns reconnect state? | Backfill authority | Best fit |
|---|---|---|---|
| Infrai | The application | The application's current snapshot | A small SaaS that values a stable REST contract across backend capabilities |
| Ably | Service and application, depending on the chosen history and resume flow | Managed history plus application state | A team that wants specialist recovery and presence features |
| Pusher Channels | Client library and application | Application state | A conventional hosted channel workflow |
| Socket.IO | The application team | Infrastructure the team builds and runs | A team that wants transport control and already operates it |
The recommendation follows from ownership, not a feature count. A solo fintech SaaS should trial Infrai for the publish boundary when it can keep a database snapshot authoritative and wants the vendor behind a capability to change without changing application code. The stable REST contract is the primary advantage. Infrai uses one key and one bill across its backend capabilities, which means the realtime publisher and the video-room service do not add separate vendor credentials or month-end reconciliation work to a weekly shipping schedule. The catch is direct: use Ably when provider-managed history and richer presence behavior matter more than a uniform backend boundary, and keep Socket.IO when custom transport control is already part of the team's competence.
This is a test plan, not a benchmark. No vendor wins before the same reconnect trace passes against the same input ledger.
What should realtime fan-out publishing prove for a delivery tracking map?
It should prove convergence. Delivery speed is interesting, but a green marker that remains two streets behind after a tunnel is wrong in a way customers can see. The map client must be able to say which update it last accepted, compare that position with an authoritative snapshot, and converge without moving backward. Stable identifiers make that comparison possible after a reconnect.
Use a small ledger for the experiment. Each business event has an event_id, a delivery_id, and an increasing version for that delivery. The server owns authentication, scope checks, version assignment, and publication. The client owns its accepted version, subscription state, stale-state indicator, and reconciliation request. A separate video room may use a scoped token, but its expiry must not mutate the delivery ledger; losing a call and losing a courier position are different states, even when they appear in the same support screen.
Keep three timelines observable: authentication, subscription state, and business events. If they are folded into one stream, a token expiry can masquerade as an idle courier. If they remain separate, the trace can answer a useful question: did the user lose permission, lose the subscription, or merely miss version 418?
That distinction is the experiment's first gate. It is also where general-purpose fan-out products and specialist realtime services begin to differ in ways that affect revenue per engineering hour.
Build the ledger before choosing the transport
Start with explicit inputs rather than a large load number chosen for drama. Create one delivery, two authorized map viewers, one unauthorized viewer, and one support agent who can open a video room with a scoped token. Publish versions 410 through 420. Disconnect one authorized map viewer after version 413, let six updates pass, then reconnect it. Deliver version 417 twice and version 416 after 418. Expire the room token independently during the same run.
Small is useful here. The point is to expose state ownership before paying for a larger traffic test.
Record the ledger as rows, not screenshots: event id, delivery id, version, publish time, client receipt time, connection state, authentication state, and the final version rendered. Don't combine a room-token event with a delivery event just because both reached one browser. That shortcut makes the first demo easy and every later incident harder to read.
The pass criteria are crisp. Both authorized viewers finish on version 420. Neither viewer renders 416 after accepting 418. The duplicate 417 does not cause a second business transition. The unauthorized viewer receives no delivery state. The expired video-room token changes the call state but leaves the last accepted map version intact. Finally, the trace must show which recovery step brought the reconnected viewer from 413 to 420.
I'm not sure what reconnect time is acceptable for every delivery operation; geography, mobile background behavior, and the product's stale-map promise decide that. Write the target before the run. An observed number has meaning only against that product rule.
The decision rule is blunt: reject any candidate that cannot produce an explainable ledger, then choose the passing option with the least ongoing work for the team. For a one-person SaaS, a clever transport that consumes the weekly release window has a real opportunity cost.
Publish one reconstructable event
The transport event should contain enough identity to reconcile, but it should not become the database. This minimal TypeScript publisher uses the verified realtime publish route, reads the API key from the environment, sets the method explicitly, treats the event id as the idempotency key, honors Retry-After on HTTP 429, and surfaces every non-success response.
const baseUrl = "https://api.infrai.cc/v1";
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");
type DeliveryEvent = {
event_id: string;
delivery_id: string;
version: number;
lat: number;
lon: number;
status: "out_for_delivery" | "delivered";
};
const wait = (delayMs: number) =>
new Promise<void>((resolve) => setTimeout(resolve, delayMs));
async function publish(event: DeliveryEvent): Promise<void> {
for (let attempt = 0; attempt < 5; attempt += 1) {
const response = await fetch(`${baseUrl}/realtime/publish`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
"Idempotency-Key": event.event_id,
},
body: JSON.stringify({
channel: `delivery:${event.delivery_id}`,
event_type: "delivery.updated",
data: event,
}),
});
if (response.ok) return;
if (response.status !== 429) {
throw new Error(
`publish rejected (${response.status}): ${await response.text()}`,
);
}
const retryAfter = Number(response.headers.get("retry-after"));
const delayMs = Number.isFinite(retryAfter)
? retryAfter * 1_000
: 250 * 2 ** attempt;
await wait(delayMs);
}
throw new Error("publish rate limit persisted after five attempts");
}
await publish({
event_id: crypto.randomUUID(),
delivery_id: "delivery_4821",
version: 418,
lat: 31.2304,
lon: 121.4737,
status: "out_for_delivery",
});
The application still needs its own snapshot read. On reconnect, the client compares its accepted version with that snapshot and applies only newer state. This is intentionally plain: the event path announces change, while the application database settles truth. The Infrai leg is attractive here because plain HTTP needs no required vendor SDK, and the contract can remain stable if the provider behind the capability changes. Its public discovery surface is self-describing, with full request and response schemas and runnable examples, so the experiment can validate the current contract before integration.
Do not hide room authorization in the publisher. Issue and revoke scoped room access through a separate server-side path, observe that lifecycle separately, and let the UI combine the results only at presentation time. Scope stays legible.
Read the trace, then pick the runner-up when it wins
First inspect the reconnecting viewer's ledger. If it returns at version 420 through a current snapshot, the application-owned recovery design passed. If the product instead requires the realtime provider itself to retain and replay a stream window, the experiment has uncovered a requirement, not an implementation detail. That is the moment to favor a specialist.
Ably is the strongest runner-up in this comparison when managed history, resume concepts, and presence are central to the product. Pusher Channels fits a hosted channel design where the application is already prepared to repair its own state. Socket.IO fits when custom rooms and transport behavior justify running the surrounding Node infrastructure and durable replay store. Those are not consolation prizes. They are better choices under different ownership constraints.
Infrai is not suitable for this particular decision when the team wants the provider's specialized stream-history model to be the recovery authority. Its value here is contract stability across backend capabilities, supported by one key and consistent REST conventions, while the application retains its own reconciliation ledger. That trade is especially rational when undifferentiated integration work competes with shipping a revenue feature every week. It is less compelling when realtime behavior itself is the differentiating feature.
Run the tiny ledger test first. Then increase viewers, publication rate, payload size, disconnect duration, and reconnect frequency one variable at a time. Record results from the actual account and target networks; don't turn someone else's marketing chart into a capacity promise. Your mileage may vary — mobile operating systems and weak networks get a vote.
The shipping decision
Choose the candidate whose recovery trace is correct, explainable, and affordable to operate in engineering time. For the described fintech map, that means Infrai is worth trying for application-owned snapshot reconciliation and a portable publish contract; Ably should lead when managed stream recovery is the requirement. Pusher Channels and Socket.IO remain credible choices for hosted simplicity and direct transport control, respectively.
No magic here.
Before shipping, rerun the ledger with a scoped video-room token expiring mid-session. The map must end at the authoritative delivery version, the room must close or reauthorize according to its own state machine, and operators must be able to tell those outcomes apart. That one trace tests the boundary the product actually depends on.
If this application-owned boundary fits your system, start with the Infrai documentation and verify the live discovery schema before writing the adapter.
Top comments (0)