Prompt and context
A collaboration app sends reliable session configuration, live cursor positions, and short-lived control hints over one QUIC connection. Old cursor values expire quickly, so occasional loss is better than queueing; configuration must arrive reliably and in order. Design the transport mapping and explain why putting every message on a reliable stream is harmful, while DATAGRAM is not “UDP without congestion.”
RFC 9221 defines QUIC DATAGRAM frames: data uses QUIC encryption and connection context but is not retransmitted. It remains subject to QUIC congestion control and the path’s maximum UDP payload. A strong answer explains how the application handles loss, reordering, and reconnects instead of only comparing TCP and UDP.
What the interviewer evaluates
- You distinguish a reliable, ordered stream of bytes from an unreliable DATAGRAM message boundary.
- You know DATAGRAM shares handshake, authentication, and congestion control; it does not retransmit or bypass receiver capacity.
- You select a carrier by message freshness, loss tolerance, and side effects while retaining a reliable configuration channel.
- You handle
maxdatagramframe_size, MTU, congestion, reconnects, and peers that do not support DATAGRAM. - You define sequence numbers, expiry, metrics, and load tests that prove dropping stale values is safe.
Questions to clarify first
- May real-time messages be reordered or lost, or do they require at-least-once delivery, ordering, or deduplication?
- What are message size, rate, burst limits, and path MTU?
- Does the peer confirm DATAGRAM support, and does traffic pass through HTTP/3, a proxy, or CONNECT-UDP?
- After connection migration, network change, or reconnect, which state must be synchronized again?
- What is the user-visible degradation on loss, and which control messages must stay on a stream?
A 30-second answer
“Configuration sync and acknowledged control operations use reliable streams; short-lived cursor updates use QUIC DATAGRAM. DATAGRAM still uses QUIC encryption, authentication, and congestion control but does not retransmit, so the application uses a monotonic sequence and expiry time to discard stale values. Negotiate the maximum datagram size and encode within the MTU budget. If the peer lacks support or loss persists, fall back to a throttled stream or the latest snapshot. Test loss, congestion, migration, and reconnect.”
Step-by-step solution
Step 1: Build a reliability/freshness matrix
Mark configuration, permissions, and commit results as reliable and ordered; mark cursors, live positions, and recomputable hints as short-lived and lossy. Choosing DATAGRAM does not create retransmission for a logical message. A side effect that needs confirmation belongs on a stream or an application protocol with explicit reliability.
Step 2: Negotiate path capability and size
Check the peer’s maxdatagramframesize. The sender must also account for maxudppayloadsize, path MTU, encryption overhead, and middleboxes. A message over budget should be compressed, moved to a stream, or dropped; do not assume IP fragmentation is reliable. Update cached capability and version when it changes.
Step 3: Define application loss and reordering semantics
Attach a session epoch, monotonic sequence, and expiry to each short-lived update. The receiver applies only a current-epoch value that is newer and unexpired; a missing cursor does not trigger retransmission because the next value supersedes it. A control hint that changes state carries an idempotency key and uses a reliable acknowledgment path.
datagram: { epoch: 42, seq: 981, expires_at: 1753938001, cursor: [412, 208] }
stream: { epoch: 42, op_id: "cfg-17", version: 9, payload: ... }Step 4: Include congestion and backpressure
DATAGRAM and streams share QUIC congestion control; a real-time flood can starve reliable data. Bound per-session and per-class budgets, observe send failure, queueing, and RTT, and drop old positions first while preserving configuration and critical control. An unbounded queue turns loss into latency accumulation.
Step 5: Design fallback, migration, and reconnect
Enable DATAGRAM only after capability negotiation. For HTTP Datagrams, also follow the Capsule protocol and proxy requirements in RFC 9297. If the peer lacks support, a path drops data persistently, or a reconnect changes capability, switch to a throttled stream or latest snapshot. Establish a new epoch on a new connection so stale data cannot contaminate current state.
Step 6: Verify acceptable loss
Replay controlled loss, reordering, congestion, MTU changes, migration, and reconnects. Check eventual configuration consistency, cursor latency and freshness, no duplicate critical operations, and that a datagram burst cannot starve streams. Record per-class sent volume, loss, expiry drops, fallback count, RTT, and queue depth; use user-experience thresholds to decide whether DATAGRAM remains appropriate.
A strong sample answer
“I put configuration, permissions, and commit results on streams because they require ordering and acknowledgment. Cursor positions use DATAGRAM because old values expire quickly. Every position carries an epoch, sequence, and expiry; the receiver accepts only the newest value for the current epoch. The sender respects maxdatagramframe_size and MTU, drops old cursors under congestion, and never lets them consume the connection budget.”
“If the peer lacks DATAGRAM support, the proxy path cannot carry it, or loss persists after migration, I fall back to a throttled stream or latest snapshot. Critical control uses an idempotency key and reliable acknowledgment. Tests cover 5% and 20% loss, reordering, reduced MTU, network changes, and reconnects, with gates for configuration consistency, cursor freshness, fallback success, and stream tail latency.”
Common mistakes
- Treat DATAGRAM as congestion-free UDP → a burst starves reliable data → share the connection budget and apply message backpressure.
- Send non-repeatable side effects over DATAGRAM → loss leaves state uncertain → use reliable acknowledgment or an idempotent application protocol.
- Ignore
maxdatagramframe_sizeand MTU → messages fail or risk fragmentation → negotiate and bound encoding size. - Retransmit every stale value after loss → latency and congestion accumulate → use sequence and expiry to drop old updates.
- Reuse an old epoch after reconnect → stale data pollutes the new session → establish a new epoch and snapshot.
- Measure throughput but not freshness → averages look healthy while users see old state → measure end-to-end delay, expiry, and critical-message tails.
Follow-up questions and answers
Does DATAGRAM guarantee order?
No. Each DATAGRAM has a message boundary, but the application handles reordering, duplicates, and loss. Short-lived data usually uses a sequence and expiry and keeps only the newest value.
Why not use a separate UDP channel?
QUIC DATAGRAM reuses the existing handshake, authentication, encryption, and congestion control, reducing connection management. It remains constrained by QUIC path and congestion semantics, so it is not raw UDP.
Can DATAGRAM carry a large file?
No. Large files need reliable, ordered, resumable streams. Keep datagrams within the negotiated size; splitting a message makes one missing piece invalidate the whole payload.
How do you know fallback works?
Record capability negotiation, fallback reason, freshness after fallback, critical-operation success, stream tail latency, and queue depth. Drills must show that fallback does not queue short-lived updates forever or lose state that required acknowledgment.