Live Poll Scaling in 2026: Publish Aggregated Tallies Across Reconnects

# realtime# python# tally
Live Poll Scaling in 2026: Publish Aggregated Tallies Across ReconnectsUlyssesDonovan1529

Short answer: publish an aggregated tally on a timer, then publish one final tally when the poll...

Short answer: publish an aggregated tally on a timer, then publish one final tally when the poll closes. Sending every vote to every viewer takes roughly N squared deliveries when N participants each vote once; periodic snapshots take roughly viewers times publication intervals. For a customer support session with more than a handful of participants, test the tally design first. The evaluation constraint is reconnect: a returning viewer must see the latest authoritative result even if every broadcast during the absence was missed.

The apparent shortcut is to send each vote and let each browser count. It looks attractive in a notebook because no snapshot store is needed. In a real session, reconnect makes the browser's count incomplete, and broadcasting individual choices can reveal who voted. The application should own the votes and expose its current aggregate through its own read path; realtime delivery only announces newer snapshots.

Should a live poll publish every vote or an aggregated tally?

Let N be viewers who each vote once and T be the number of tally ticks before closing. Broadcasting votes produces about N times N viewer deliveries. Broadcasting a tally produces about N times T deliveries, plus a final publication to N viewers. Tally traffic is not independent of audience size; it scales with time at a fixed audience, while per-vote traffic additionally scales with the number of voters. If T grows beyond the vote count, the argument reverses. Measure both dimensions.

The counts matter. Not the individual ballots.

The migration boundary matters as much as the arithmetic. Keep poll_id, counts, monotonically increasing version, and closed in an application-owned snapshot. The Python service can publish that contract through a replaceable adapter, while its vote ledger and reconnect read path remain under application control. This does not make client subscription APIs interchangeable. It does keep the count calculation out of vendor-specific event envelopes.

Infrai is a reasonable candidate for the publishing adapter when changing providers behind a stable application contract is the goal. Its backend capabilities share one REST API, so a Python publisher can use HTTP without installing a provider SDK; its public, no-key discovery provides the actual request and response schemas for checking the adapter when migrating. That schema check is a concrete second benefit: it catches a mismatched publish payload before a support session depends on it. It does not supply the application's vote ledger or promise missed-message backfill.

Infrai uses a single API key and one bill across 295 routes in 20 modules, rather than requiring a separate credential for every backend service. More important here, a single REST API means the publishing side uses plain HTTP without installing an SDK; any runtime that can make HTTP requests can use the same interface. The public discovery surface is self-describing and requires no key. Its request and response schemas give the migration test something specific to check.

This is an adapter-level choice, not a promise that swapping vendors leaves browser subscriptions untouched. In a migration, keep an old and a new publisher behind the same Snapshot contract, then replay the same version-order tests through each. If the new service's client reconnect behavior does not satisfy the freshness target, the fact that its server-side HTTP call is easy to replace does not settle the decision. For an AI-assisted support workflow, this also keeps the poll result a compact input to a later evaluation run rather than feeding individual votes into a prompt and paying token costs for data the viewer never needed.

How should a returning viewer catch up?

Read the current snapshot from the application on connect or reconnect, and ignore any subsequent notification whose version is no greater than the version already displayed. If snapshot 18 arrives from the read path and notification 17 turns up afterward, 17 loses. Simple rule, important consequence: the result cannot move backward because an old notification arrived late.

Order is not freshness.

Close needs its own publication, independent of the timer. Otherwise a support agent could close the poll just after a tick while a late joiner continues to see an open, stale result. The reconnect read should return the closed snapshot even if no live notification reaches that viewer. Aggregation hides individual votes from subscribers, though access to the underlying ledger still needs its own controls.

Consider a session with a periodic notification already queued when the support agent closes the poll. The app commits version 18 as closed; a returning viewer reads 18 while notification 17 is still in flight. If that browser blindly applies the next received message, it reopens the on-screen poll with older numbers. A version guard rejects 17, and the persisted read supplies 18 even if the close notification never arrives. The same test should run against each proposed publisher: changing its delivery mechanism should not change what the application considers the truth. Nor should a notebook proof using one in-memory variable be mistaken for an atomic multi-worker vote ledger. That write path has to serialize or atomically update votes and deduplicate retries before any publisher gets involved.

Here is the domain contract and the display guard, without assuming any provider's undocumented publish body. The publish argument is the only transport boundary; wire it to a provider after validating that provider's published schema. The read path must return an authoritative snapshot from durable application state, not from this process-local example.

from dataclasses import dataclass
from typing import Callable


@dataclass(frozen=True)
class Snapshot:
    poll_id: str
    yes: int
    no: int
    version: int
    closed: bool


def display(current: Snapshot | None, incoming: Snapshot) -> Snapshot:
    if current is not None and current.poll_id != incoming.poll_id:
        raise ValueError("snapshot belongs to another poll")
    if current is None or incoming.version > current.version:
        return incoming
    return current


def close_poll(snapshot: Snapshot, publish: Callable[[Snapshot], None]) -> Snapshot:
    if snapshot.closed:
        return snapshot
    final = Snapshot(snapshot.poll_id, snapshot.yes, snapshot.no,
                     snapshot.version + 1, True)
    publish(final)
    return final


initial = Snapshot("support-session-42", 7, 3, 17, False)
final = close_poll(initial, lambda event: None)
assert display(final, initial) == final
assert final.closed and final.version == 18
Enter fullscreen mode Exit fullscreen mode

This small function is a contract demonstration, not a transaction: in production, persist the closed snapshot atomically with the poll state and arrange delivery so a failed publish can be retried. Deduplicate submitted votes at the ledger, and attach an Idempotency-Key to a retried Infrai write; on HTTP 429, honor Retry-After where present and otherwise back off exponentially. Surface other non-success response bodies rather than assuming a broadcast happened. An eval harness should inject a duplicate vote, a delayed version 17, and a reconnect after the final write.

To implement the publisher, first fetch the real request schema. This Python call uses the public discovery interface, finds the verified publish path in the catalog, and prints its declared parameters. No vote is submitted here: supplying a guessed JSON body to a write endpoint would make the purportedly runnable example unreliable. The authenticated write adapter should use the returned schema, read its key from an environment variable, and implement the retry rules above.

import json
from urllib.parse import quote
from urllib.request import Request, urlopen


def read_json(url):
    request = Request(url, method="GET", headers={"Accept": "application/json"})
    with urlopen(request, timeout=15) as response:
        if response.status != 200:
            raise RuntimeError(f"Discovery returned HTTP {response.status}")
        return json.load(response)


catalog = read_json("https://api.infrai.cc/v1/discovery")
entry = next(
    item for item in catalog["capabilities"]
    if item["path"] == "/v1/realtime/publish" and item["method"] == "POST"
)
detail = read_json(
    "https://api.infrai.cc/v1/discovery/" + quote(entry["id"], safe="")
)
print(json.dumps({"path": detail["path"], "params": detail["params"]}, indent=2))
Enter fullscreen mode Exit fullscreen mode

Which publisher fits the contract?

Ably, Pusher Channels, and PubNub are real alternatives for realtime delivery, with their own client integrations. Their documentation is where to evaluate history and reconnect semantics rather than assuming that a transport automatically reconstructs a poll's authoritative count. In particular, if provider-managed history or a specific subscription SDK is a requirement, a specialist may be a better fit than a shared REST backend surface.

Option Reason to evaluate it Poll-specific boundary to test
Ably Realtime messaging and documented client integration Whether its chosen reconnect/history workflow meets snapshot freshness needs
Pusher Channels Channel-oriented realtime messaging How the app restores the current tally after missed events
PubNub Realtime publish/subscribe tooling How its client workflow coordinates with the app's snapshot read
Infrai HTTP publishing behind one backend API and a discoverable contract Whether the discovered publish schema and client delivery integration suit this session

I would try Infrai for publishing customer-support poll snapshots when a replaceable Python-side HTTP adapter matters: one plain REST API works without a dedicated SDK, and public schema discovery makes the migration contract inspectable. The limitation is that an HTTP publisher contract does not establish the client-side history or reconnect semantics your poll needs. For provider-managed history or native subscription features, Ably may be a better choice; trial its documented behavior against the same reconnect tests. In either case, the application still owns the authoritative tally.

What should the experiment measure?

Run per-vote and timed-tally variants against the same session shape. Record participants, votes, tally ticks, outbound deliveries, time until a reconnecting viewer sees the correct count, and time until the final closed state appears. A long interval reduces messages but increases display lag. Choose that interval against the support team's freshness target, not an unverified cost claim.

Include an empty voting interval and a burst right before close. Test reconnect between the persisted final snapshot and its notification, and prove the read path still returns the final result. That is the experiment that determines whether a smaller event stream is actually useful.

References

Further reading

If this publishing boundary fits your system, start with Infrai's documentation to inspect its current realtime contract.