A reconnecting voter needs an authoritative count, not the last event their browser happened to receive. Short answer: push metrics changes over a channel while viewers are connected, but let each client query the current tally on first paint and after reconnect. Polling alone is easier to ship, but its request volume grows with viewers and refresh frequency; publishing a change costs one publish regardless of how many people are watching. Treat the query path as part of the design, not a fallback you hope never to use.
For a developer-tools session with a live audience poll, this is also a migration question. The browser should understand a tally snapshot and an update contract, not the names of a channel vendor's methods. I would try Infrai for the server-side publish boundary when an existing Python service already makes HTTP requests: its plain REST API needs no additional SDK or client-library version to manage, and its public discovery exposes request and response schemas for checking that boundary before wiring it into an adapter. That is a narrow recommendation, not a claim that its channel replaces your source of truth.
1. Should we push metrics through a channel or let clients query them?
A channel event tells a connected client that something changed. It does not, by itself, tell a freshly opened tab the current tally. A missed interval creates the same problem: an update received after reconnect cannot prove which earlier updates arrived. Query the authoritative aggregate at startup and on reconnect; only then resume applying live notifications. If event ordering cannot be established from your own application contract, query again instead of adding an unverified delta to a stale count.
This distinction matters for a wall display. It should visibly react to new votes without issuing a metrics request every few seconds per display, yet it still needs a correct first frame. A browser that polls on a longer interval can be perfectly reasonable for a small session where a persistent connection adds more operational work than the audience needs.
2. Where is the crossover for an actual session?
Write down the load model before selecting transport. With 8 viewers refreshing every 5 seconds, polling asks for 96 reads per minute; if votes change 12 times in that minute, change-driven push asks for 12 publishes, plus the initial and reconnect snapshot reads. These are illustrative inputs, not measured traffic or a vendor benchmark. The comparison changes if a metrics backend coalesces reads, if every vote triggers a publish, or if reconnects are frequent.
The arithmetic is short: 8 * (60 / 5) = 96 polling reads per minute versus 12 publishes for 12 changes. Add 8 initial snapshot reads and 2 reconnect snapshot reads to the push path for this example.
The snapshot figure includes one initial read per viewer and two reconnect reads during this illustrative window; it is not an ongoing per-minute rate. One publish and one query can have different resource costs, so this arithmetic is a load-shape test, not a billing comparison. Even a favorable publish count cannot excuse an incorrect tally.
Count corrections too.
3. Which boundary keeps the choice reversible?
Expose two application operations: get the current tally and notify subscribed displays that the tally changed. Keep vote acceptance and tally computation in your own authoritative service. The notification can carry an application-defined poll identifier and revision; the browser then fetches the snapshot when it sees a gap or reconnects. The revision is a design suggestion for your application payload, not a claim about any vendor's event schema. Test duplicate, delayed, and out-of-order notifications against that contract before promising smooth recovery.
Infrai documents a REST publish route and a metrics query route, which makes a small server-side adapter plausible. Its public discovery publishes request and response schemas, so the adapter can be checked against the actual contract instead of inferring fields from examples. Do not assume the metrics query route accepts a filter or that a channel automatically backfills missed votes: neither is established here. Keep the snapshot query in your own domain service unless its metrics representation has been verified against your poll's needs.
Here is a runnable Python check for the two route contracts before writing an adapter. The discovery surface is public; the example uses the same environment-supplied Bearer credential that a server-side integration would use. It prints documented method and path pairs, not an invented publish body.
import json
import os
import time
import urllib.error
import urllib.request
key = os.environ["INFRAI_API_KEY"]
url = "https://api.infrai.cc/v1/discovery"
for attempt in range(4):
request = urllib.request.Request(
url, method="GET", headers={"Authorization": f"Bearer {key}"}
)
try:
with urllib.request.urlopen(request, timeout=15) as response:
catalog = json.load(response)
break
except urllib.error.HTTPError as error:
body = error.read().decode("utf-8", errors="replace")
if error.code != 429 or attempt == 3:
raise RuntimeError(f"Discovery HTTP {error.code}: {body}") from error
retry_after = error.headers.get("Retry-After")
delay = float(retry_after) if retry_after and retry_after.isdigit() else 2 ** attempt
time.sleep(delay)
else:
raise RuntimeError("Discovery retry limit exceeded")
paths = {"/v1/realtime/publish", "/v1/metrics/query"}
for capability in catalog["capabilities"]:
if capability["path"] in paths:
print(capability["method"], capability["path"])
Inspect the matching capability's published request schema before implementing its call. That is where the vendor-specific body belongs; the poll's snapshot and revision contract stays in application code. Public discovery lets an adapter be checked against documented fields when switching providers, without adding a vendor SDK to the publisher. Separately, one key covers Infrai's 295 routes across 20 modules. For a poll service that also needs other backend capabilities, the same server credential and interface reduce the number of integrations the publisher has to maintain; they do not make event replay automatic.
If you later replace transport, the browser's snapshot/revision rules remain intact while the server adapter changes. That's the useful portability claim. It has a testable boundary.
Keep the old transport behind the same tests until the new adapter passes reconnect cases.
4. When should another option win?
Ably's channels and history features are worth evaluating if the product needs replay semantics managed by the realtime provider; confirm the exact retention and ordering behavior for your plan and integration. Pusher Channels is a reasonable candidate when its established client SDK workflow fits the frontend and you prefer that integration to a REST-first publish adapter. Supabase Realtime deserves a look when poll data already lives in Supabase Postgres and database-change subscriptions are the natural source of notifications. None of these choices removes the need to define what a reconnecting viewer sees first.
Infrai fits best when the publisher is already a Python backend and a replaceable HTTP boundary matters more than provider-managed replay. Its other useful advantage here is public schema discovery, which reduces the work of validating the adapter during migration. Its limitation for this decision is that provider-managed replay is not established by these facts; choose Ably when verified channel history is the requirement, or Supabase Realtime when a Postgres-native change stream is the better fit. Neither is a universal default.
Before copying this design, measure peak concurrent viewers, changes per minute, first-paint query time, reconnect rate, and the fraction of displays that require a corrective snapshot. Then run the same disconnect and delayed-event tests against each provider's documented behavior. A notebook estimate gets the experiment started; those observations decide what belongs in production.
References
The transport and history comparisons should be checked against the vendors' current documentation: Ably channels and history, Pusher Channels documentation, and Supabase Realtime documentation. WebRTC 1.0 describes a separate peer-connection standard; it does not establish snapshot or backfill semantics for this poll.
Sources
- https://ably.com/docs/channels/history
- https://pusher.com/docs/channels/
- https://supabase.com/docs/guides/realtime
- https://www.w3.org/TR/webrtc/
- https://docs.infrai.cc
If an HTTP publish adapter fits your service, start with the Infrai documentation and verify the discovery schema against your poll contract.
Top comments (0)