DEV Community

KillianBerg5391
KillianBerg5391

Posted on

Python Poll Message Size: Publish Per Waiter Versus Whole Queue

Send one compact poll-state update per changed answer, then let clients recover from a versioned snapshot. Short answer: for a live classroom poll, broadcasting the entire response queue after every submission multiplies payload bytes by both the number of submissions and the number of viewers. Publishing one message per participant avoids that multiplication only if recipients actually need individual records; most viewers need aggregate counts instead. The delivery guarantee matters more than the first byte comparison: a missing delta needs a detectable gap and a snapshot repair path.

A Python app can keep the notebook-friendly tally calculation while production delivery has explicit contracts. A learner submits an answer with a stable submission ID. The server records it, increments a monotonically increasing poll version, and emits an aggregate update. Connected displays apply newer versions; a reconnect fetches the current state. Individual answers stay on an authorized instructor channel, if that channel is needed at all.

Should we publish per waiter message or the whole queue?

Suppose an illustrative session has 120 learners, four choices, and 121 displays including the instructor. These are design inputs, not measured traffic. If one serialized answer record takes 80 bytes, a 120-record snapshot is roughly 9,600 bytes before framing. Sent to 121 displays after each of 120 submissions, that is about 139 million application-payload bytes. An 80-byte changed-record message sent to the same displays would be about 1.16 million bytes. But that comparison assumes every display should see each learner's answer. Usually it should not. An aggregate message with four counts, a poll ID, and a version can be measured directly with the same serializer and typically remains independent of class size.

The calculation is a planning aid, not a transport benchmark. Serialization, metadata, compression, retransmission, and connection framing change wire bytes. More importantly, a full snapshot is self-healing after one missed message, while an isolated delta is not. That's the trade. Measure a realistic serialized payload before selecting a fan-out pattern, then test what happens when update 18 arrives after update 20.

Count recipients too.

Can a small Python example preserve delivery order?

This runnable standard-library example models the payload and client-side version check. It does not claim that an in-memory dictionary is a durable database or a broadcast system. The sender's transaction must commit the answer and an outbox record atomically before a separate worker sends anything; otherwise a crash between storage and publish can silently lose an update.

import json


def encoded_size(value):
    return len(json.dumps(value, separators=(",", ":")).encode("utf-8"))


def accept_update(current, incoming):
    if incoming["version"] <= current["version"]:
        return current, "duplicate_or_old"
    if incoming["version"] != current["version"] + 1:
        return current, "fetch_snapshot"
    return incoming, "applied"


poll = {"poll_id": "lesson-42", "version": 20, "counts": [24, 36, 40, 20]}
answer = {"poll_id": "lesson-42", "version": 21, "learner_id": "student-17", "choice": 2}
next_poll = {"poll_id": "lesson-42", "version": 21, "counts": [24, 36, 41, 20]}
print("individual bytes:", encoded_size(answer))
print("aggregate bytes:", encoded_size(next_poll))
print(accept_update(poll, next_poll))
print(accept_update(poll, {**next_poll, "version": 22}))
Enter fullscreen mode Exit fullscreen mode

The example deliberately separates a sensitive answer record from a viewer-safe aggregate. A real tally also needs a rule for changed answers: replace a prior choice under the same learner-and-poll key, and update both affected counts in the same transaction. Idempotency on the submission ID handles retries; the poll version handles ordering at the receiver. Neither one substitutes for the other.

Where do guarantees fail during fan-out?

An acknowledgment from a publish API does not prove that 121 browsers rendered the update. A disconnected display can miss a delta, and two workers can deliver versions out of order. Define a committed-state guarantee first: each accepted submission produces one durable tally transition. Then define a display guarantee separately: clients may temporarily lag, but detect a gap and reload an authoritative snapshot before showing a later version.

For an instructor dashboard that must inspect individual responses, authorize that stream separately and avoid embedding identifiers in the classroom display event. For the shared display, send versioned aggregate state. Sending a full aggregate snapshot on each change may be perfectly reasonable when there are only four counts; the costly pattern is repeatedly distributing an ever-growing list of individual records. The payload shape, not the word 'snapshot,' determines the byte curve.

The limitation of an aggregate-only stream is concrete: an instructor cannot use it to identify a learner who needs help, and a count alone cannot explain a disputed answer change. That workflow requires a separate access-controlled record view and a defined retention policy. Conversely, an individual-answer stream is inappropriate for a projected classroom display because it exposes unnecessary identities and forces each viewer to reconstruct the tally correctly. Full queue snapshots remain useful for bounded administrative views or initial synchronization, but their size grows with the number of records. A versioned tally reduces routine fan-out bytes at the cost of explicit gap detection and recovery.

There is no universal winner.

If a poll closes while an update is in flight, include status and version in the fetched snapshot. Clients discard stale events, rather than reopening the poll based on arrival order. This is also where an eval-minded workflow helps: replay duplicate submissions, late versions, disconnects, and answer changes against the tally invariant before testing network throughput. Keep an eye on bytes per accepted answer, fan-out recipients, gap-repair count, and time from committed version to displayed version. The prompt-cost lesson applies here too: measure the actual artifact before optimizing an assumed bottleneck.

What should ship with the poll?

Start with one poll ID, an idempotent submission key, a transactionally maintained tally and version, and a snapshot endpoint protected by the same access rules as the live channel. Exercise reconnect and gap repair in staging with the intended class size. Track serialized payload sizes separately for instructor and learner views, plus delivery lag and repair frequency; these numbers expose both unnecessary per-learner fan-out and broken recovery. Once the contract holds, choose transport and deployment topology according to your existing operations skills. The decision is about what a viewer is allowed to know, how many bytes each committed change generates, and how a missed message gets repaired.

References

Top comments (0)