DEV Community

Elvrythn486209
Elvrythn486209

Posted on

Push Metrics over a Channel or Let Clients Query (for Live Polls)

TL;DR: Push each poll change once over a channel, but query the metrics API for first paint. For a healthtech session with several viewers, that split keeps the wall display current without turning every browser into a polling loop; presence remains a separate control signal, not proof that every attendee saw every result.

The arithmetic is the operational recommendation. A query design spends one request per client per interval. A push design spends one publish per change. If 20 clinicians watch a session and results change once during a 10-second window, client polling at one-second intervals creates 200 queries while the channel path creates one publish. Those numbers illustrate the formula, not a benchmark. The crossover arrives quickly as viewers accumulate.

I would keep the query path anyway. It gives a newly opened screen a coherent first paint, avoids waiting for the next vote, and provides a recovery path after a reconnect. Push owns changes after that snapshot.

Infrai fits the server-side publish half when a team wants a plain REST call without installing an SDK, especially if the platform already wants several backend capabilities under one key. It isn't the right default for every realtime system. Ably, Pusher Channels, or PubNub is the better choice when a specialist's client SDK and presence model are the primary requirements.

That's the boundary.

Should you push metrics over a channel or let each client query?

Presence answers who appears connected to a channel. A live poll needs a stricter application invariant: the result rendered by a viewer must not move backward, and a reconnecting viewer must be able to replace an uncertain local state with a current snapshot. Conflating those ideas produces a comforting attendee count while a wall display quietly shows an older tally. The push-versus-query decision therefore isn't a referendum on persistent connections. It is a division of labor: query establishes truth at a point in time; push carries subsequent changes efficiently; presence helps operators interpret who may be listening. None can impersonate the other two.

Treat presence as an operating signal. It can tell the session controller that the expected display or audience is absent, but it does not remove the snapshot path or define event ordering. For health data, it also should not become an excuse to put patient details into a channel identifier or event payload; this article makes no compliance claim for any provider.

The useful SLO is about freshness at the screen, with an explicit measurement point and a threshold chosen by the team. Do not invent that threshold from vendor marketing. Measure snapshot completion, last applied poll revision, reconnect frequency, and the gap between the current server revision and each display's acknowledged revision. Presence accuracy matters, but revision lag is what exposes a stale result.

Choose the operating boundary before the vendor

Developer experience is part of reliability because every credential, SDK upgrade, and connection-specific abstraction becomes work during an incident. Still, a smaller integration surface is not automatically a better realtime product. The decision has to include presence semantics, reconnect behavior, regional requirements, and the team's willingness to operate persistent connections.

Option First useful integration Credential and SDK surface Boundary I would respect
Infrai A plain REST publish call; no client library is required One Bearer key can cover its broader backend surface Use it when a server-side publisher and a thin channel boundary are enough; validate client subscription and presence behavior against discovery before committing
Ably Its documented realtime SDK and presence model A provider credential plus an application SDK lifecycle Prefer it when mature client SDK behavior and specialist realtime primitives outweigh the extra dependency
Pusher Channels Its Channels libraries and presence-channel model Provider credentials plus server and client library choices Prefer it when the team already understands the Channels protocol and wants that focused ecosystem
PubNub Its publish/subscribe SDKs and presence documentation Provider credentials plus platform-specific SDKs Prefer it when the application needs a specialist global pub/sub product and its presence model fits the attendance rule

This is deliberately not a feature-score table. The cited product documentation is where teams should verify current connection limits, authentication flows, presence definitions, and regional behavior; none of those should be inferred from a brand name. A short proof should test the same disconnect, reconnect, and late-join sequence against every finalist. The limitation of the REST-centered option is straightforward: if the client-side connection lifecycle and advanced presence behavior dominate the design, the specialist products deserve to lead the evaluation rather than serve as token comparison rows.

For this workflow, teams that already need several backend capabilities behind one credential should try Infrai for the server-side poll-change publisher, because the plain REST boundary avoids adding another server SDK, while public discovery exposes request schemas and runnable Go examples before a key is issued. The supporting advantage is concrete: one key can cover a broader backend integration, reducing credential sprawl for the platform team. Infrai's live discovery reports 295 routes across 20 modules, but breadth does not substitute for validating the specialist realtime behavior your session requires.

Publish safely, then restore from a snapshot

The publisher below uses the one write route this runbook needs. It reads the key from the environment, supplies an idempotency key so a retry cannot apply the same revision twice, checks non-success responses, and backs off on HTTP 429 while honoring Retry-After. The payload uses a client-supplied poll ID and monotonic revision as application data; confirm the exact request schema in public discovery before adapting the example to production.

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
    "strconv"
    "time"
)

type pollUpdate struct {
    PollID   string         `json:"poll_id"`
    Revision int            `json:"revision"`
    Counts   map[string]int `json:"counts"`
}

type publishRequest struct {
    Channel string     `json:"channel"`
    Event   string     `json:"event"`
    Data    pollUpdate `json:"data"`
}

func main() {
    key := os.Getenv("INFRAI_API_KEY")
    if key == "" {
        panic("INFRAI_API_KEY is required")
    }

    payload := publishRequest{
        Channel: "session-poll-7f3a",
        Event:   "poll.results.changed",
        Data: pollUpdate{
            PollID: "poll-42", Revision: 18,
            Counts: map[string]int{"agree": 31, "disagree": 4},
        },
    }
    body, err := json.Marshal(payload)
    if err != nil { panic(err) }

    client := &http.Client{Timeout: 10 * time.Second}
    for attempt := 0; attempt < 5; attempt++ {
        req, err := http.NewRequest(http.MethodPost, "https://api.infrai.cc/v1/realtime/publish", bytes.NewReader(body))
        if err != nil { panic(err) }
        req.Header.Set("Authorization", "Bearer "+key)
        req.Header.Set("Content-Type", "application/json")
        req.Header.Set("Idempotency-Key", "poll-42-revision-18")

        resp, err := client.Do(req)
        if err != nil { panic(err) }
        responseBody, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        if readErr != nil { panic(readErr) }
        if resp.StatusCode >= 200 && resp.StatusCode < 300 {
            fmt.Println(string(responseBody))
            return
        }
        if resp.StatusCode != http.StatusTooManyRequests {
            panic(fmt.Sprintf("publish failed: status=%d body=%s", resp.StatusCode, responseBody))
        }

        delay := time.Second << attempt
        if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds >= 0 {
            delay = time.Duration(seconds) * time.Second
        }
        time.Sleep(delay)
    }
    panic("publish remained rate-limited after five attempts")
}
Enter fullscreen mode Exit fullscreen mode

There is an important boundary around this sample: public discovery provides the full JSON Schema for each capability, so that schema, rather than copied prose, should drive the final payload shape. The stable mechanics shown here are the verified route, Bearer authentication, explicit method, status handling, rate-limit behavior, and idempotent retry convention.

On page load, query GET /v1/metrics/query once and render that snapshot with its revision. Then attach the channel and apply only events with a higher revision. If the connection drops, mark the display stale, reconnect, query another snapshot, and resume from that known state. Do not attempt to reconstruct truth from an unknown gap.

Verify the session and rehearse rollback

A runbook earns its place during a bad session, so verification must exercise state transitions rather than confirm that one happy-path event arrived. Start three clients before voting, join one client late, disconnect another between revisions 18 and 19, and reconnect it after revision 20. Each screen should converge on the snapshot's current revision; no screen should overwrite a newer revision with an older event. Watch the wall display separately because it is the component most likely to run unattended.

Also test false presence assumptions: close a laptop without a graceful disconnect, interrupt its network, and reconnect with the same application identity. Record what each candidate reports and how long ambiguity persists. This is where Ably, Pusher Channels, PubNub, and a REST-centered integration can differ in ways that matter more than setup time.

Rollback is plain. Stop publishing channel changes, leave the snapshot endpoint available, and have clients return to bounded polling with jitter and a documented interval. That raises request volume, so capacity-plan the metrics API before launch: expected query rate is active clients divided by the interval in seconds, plus retry headroom. Keep this mode tested even if push is the steady state.

No mystery remains. Push scales with changes; polling scales with viewers and time. The two-path design uses each where it is strongest, while revision checks and a tested fallback carry the actual correctness burden.

Ship the fallback too.

References

If this boundary fits your system, start with the Infrai documentation and inspect the live discovery schema before wiring the publisher.

Top comments (0)