DEV Community

LiamFoster1844
LiamFoster1844

Posted on

Pushing Beats Polling Explained: Live Poll Trust Above a Handful of Viewers

Short answer: pushing beats polling above a handful of viewers because one state change should not trigger repeated reads for every screen, but treat delivery as an optimization, not as authorization. Scope every subscription token to one session, keep vote acceptance on a trusted server path, and retain a slow refresh route for recovery. Polling is operationally attractive at first because its failure mode is obvious; its repeated reads, synchronized timers, and stale windows become the wrong load shape as viewership grows.

This is a capacity decision before it is a pricing decision. The cost mechanism is explained by duplicated work, not by a vendor's changing unit price. For a marketplace running a live poll during a seller session, ask how much duplicate work enters the system when one result changes, what a reconnect storm can do to the SLO, and what authority a browser acquires when it joins a room.

Why does pushing beat polling above a handful of viewers?

Let V be connected viewers, I the refresh interval in seconds, and D the session duration. A polling design creates roughly V * D / I result reads even when nobody votes. With 6 viewers, a 5-second interval, and a 60-second observation window, that is 6 * 60 / 5, or 72 reads; the arithmetic is illustrative, not a benchmark. One result change has still occurred at most once in this example. A push design needs connection maintenance and fan-out, but an actual poll change can be represented once and distributed to the viewers already subscribed. Add viewers and the polling term rises linearly even during a quiet poll. Shorten the interval and it rises again. That distinction matters more than a transient per-request price: polling couples database and cache demand to audience size and wall-clock time, while push couples most application work to state changes and connection count.

The stale-data bound is also structural. A 5-second polling interval can leave a healthy client nearly 5 seconds behind; shortening the interval reduces that window by deliberately increasing read pressure. Push removes that particular trade-off, although it introduces connection lifecycle work, backpressure, and reconnect behavior.

No free lunch.

Capacity planning should therefore keep 4 separate budgets: concurrent connections, delivered messages per state change, reconnect attempts per second, and authoritative writes. Do not hide all 4 behind a single request-rate graph. A dashboard can look calm while disconnected clients retry together, and averages will conceal the burst that consumes the error budget. The trade-off is explicit: accept connection-state complexity to avoid recurring read amplification and a built-in freshness delay.

Put trust outside the transport

A realtime channel answers where an update travels. It does not answer who may vote, which session they may observe, or whether a submitted option is valid. The browser is an untrusted client even when its connection is encrypted.

Trust does not fan out.

Issue a short-lived subscription credential whose claims identify the session and permit only the minimum read capability. The server that accepts a vote must independently authenticate the actor, verify that the poll belongs to the session, validate the option, enforce the poll's open state, and apply the marketplace's duplicate-vote rule. Never infer write permission from membership in a delivery room.

Keep public poll state deliberately small: a poll identifier, revision, status, option totals, and server timestamp may be enough. Buyer identity, seller controls, moderation flags, and raw ballots belong on narrower paths. This separation is useful even if the first implementation runs in one process, because it creates an auditable boundary before infrastructure choices make the boundary expensive to change.

Implement revisioned snapshots

The safest small protocol sends complete public snapshots with a monotonically increasing revision. Deltas save bytes, but they force every reconnecting client to prove that it has an unbroken history. Poll totals are compact, so a snapshot is often the more defensible starting point. The following Go types and checks show the contract without binding it to a commercial service.

package poll

import (
    "errors"
    "time"
)

type Claims struct {
    SessionID string
    CanRead   bool
    ExpiresAt time.Time
}

type Snapshot struct {
    PollID     string         `json:"poll_id"`
    Revision   uint64         `json:"revision"`
    Status     string         `json:"status"`
    Totals     map[string]int `json:"totals"`
    ServerTime time.Time      `json:"server_time"`
}

func AuthorizeSubscription(c Claims, sessionID string, now time.Time) error {
    if !c.CanRead || c.SessionID != sessionID || !now.Before(c.ExpiresAt) {
        return errors.New("subscription denied")
    }
    return nil
}

func ShouldApply(current, incoming uint64) bool {
    return incoming > current
}
Enter fullscreen mode Exit fullscreen mode

The client applies only a newer revision. Equal revisions are harmless duplicates; older ones are discarded. After a disconnect, it requests a fresh authorized snapshot before consuming subsequent pushes. That gives reconnect a clear correctness rule rather than hoping message order survived a broken connection.

WebRTC data channels are one standards-based option for bidirectional peer data, and the W3C specification defines their API and connection model. They do not erase the authorization, fan-out, or recovery work described here. A relay-oriented or server-mediated design is usually easier to reason about for authoritative marketplace totals because the trusted service remains the source of published state. The transport choice should follow topology and operational constraints, not fashion.

Buy or build the delivery plane?

Use an SLO and an ownership boundary before comparing implementations. A managed delivery plane can reduce connection operations; self-hosting can increase control over placement and protocol behavior. Either choice still leaves the application team responsible for claim scope and vote correctness.

Decision area Managed delivery Self-hosted delivery Evidence to collect
On-call load Connection fleet is delegated Team owns upgrades, saturation, and failover Pages, recovery time, dependency objectives
Control Product limits shape topology Protocol and placement are adjustable Required regions, fan-out pattern, retention needs
Lock-in Client and auth contracts may be specific Internal interfaces can still become sticky Exit test with a second adapter
Capacity Quotas and billing dimensions constrain growth Hosts, file descriptors, and egress constrain growth Peak connections and reconnect rate

Do not decide from the smallest invoice. Estimate steady connections, peak fan-out, authentication calls, egress, storage reads, and engineering coverage, then test the estimate at the concurrency and churn expected for a popular session. A platform with comfortable steady-state headroom can still miss its availability objective when many clients reconnect at once.

Verify, degrade, and roll back

Test correctness before scale. Open two clients with differently scoped credentials, attempt a cross-session subscription, submit duplicate and late votes through the authoritative path, disconnect one viewer, change the poll, and confirm that its recovered snapshot has the newest revision. Then load-test connection establishment separately from message fan-out; they stress different resources.

Observe subscription denials, connection establishment latency, concurrent connections, publish-to-apply latency, revision gaps, snapshot refreshes, reconnect rate, and authoritative vote errors. Define the user-facing SLO around fresh visible poll state, not around a broker acknowledging a publish. The latter can be green while clients remain stale.

Rollback should be boring. Keep the authenticated snapshot endpoint available, add jitter to refresh intervals, cap retries, and let clients fall back to slow polling when the delivery channel is unavailable. The fallback protects continuity, but it must have a capacity ceiling; otherwise an outage in the push plane merely transfers a synchronized surge to the read plane. Disable live updates independently of vote submission so the marketplace can preserve correct participation while showing results less quickly.

The operating rule is compact: push revisions for freshness, authorize every boundary, and recover from snapshots. Choose managed or self-hosted delivery only after the team can state who carries the pager, which SLO is being purchased, and how the system exits that choice.

References

Top comments (0)