fix(moq-pub-mmtp): backport reconnect-with-backoff onto pinned live-edge baseline 21006781 (BLO-26173) - #79
Conversation
…26173) Backport of #76 (reconnect loop) + #78's publisher-local backoff reset onto the focused live-edge baseline 2100678, scoped strictly to moq-pub-mmtp/**. Production `blockcastd-moq-pub-mmtp` is digest-pinned to a moq-rs commit that carries only the four-MTU oldest-drop queue bound on the prior protocol baseline, deliberately without the broad moq-rs upgrade (onprem-k8s production-values.yaml:1221, authored by Omar Ramadan 2026-08-06). Gateway main's pin is 165 commits ahead of that and moves moq-transport's request-ID handling and draft-19 wire tests — the exact surface whose mismatch reingests the "session error: invalid request ID" crashloop. So the reconnect fix is backported here rather than shipped by re-pinning. What changed: - The relay session is established inside a retry loop with exponential backoff (--reconnect-backoff-min-ms 500 .. --reconnect-backoff-max-ms 30000) instead of propagating the error out of main and exiting 1. - The input source is opened ONCE and held across every reconnect. For --mmtp-input=udp that keeps the SSM (S,G) join alive: the join is a property of the socket, not of the moq-transport session. - Packet ingest moves from a third spawned task into the session select!, so the source is owned by main across reconnects. Ingest remains a single ordered flow, so datagram.rs's monotonic group_id assignment is unchanged. - A session that outlives the backoff ceiling clears the consecutive-failure streak, so a publisher that has dropped N times over its lifetime does not stall the full ceiling on every later blip (#78). - Clean stdin EOF still exits 0 rather than reconnecting forever. Deliberately NOT backported: - The `moq_pub_session_reconnect_total` counter. `metrics` is not a dependency of moq-pub-mmtp at this baseline and metrics_endpoint.rs does not exist here; adding them pulls in the observability stack this backport exists to avoid. Reconnects are observable via the structured task=/attempt=/backoff_ms= log fields instead, which satisfies BLO-26173's "counter and/or log at info". - #78's moq-transport/src/serve/subgroup.rs binary-search change. It is a pure read-path optimization, unrelated to reconnect, and touching moq-transport is precisely what the pinned pub/relay pair forbids. `git diff 2100678..HEAD -- moq-transport/` is empty by construction. Co-Authored-By: Paperclip <noreply@paperclip.ing>
|
🔗 Paperclip issue: BLO-26173 |
1 similar comment
|
🔗 Paperclip issue: BLO-26173 |
A PR based on release/pinned-live-edge-21006781 got zero check runs: the pull_request trigger filtered on base == main only. That is the same silent no-CI condition that let 54d855f merge a tree failing `cargo test -p moq-pub-mmtp` (see moq-pub-mmtp/src/metrics_endpoint.rs's note). A pinned-baseline lane is exactly where an unbuilt merge is most dangerous: the whole point is that production runs that ref, not main. Co-Authored-By: Paperclip <noreply@paperclip.ing>
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: aca78a0
Critical Issues (0)
Important Issues (1)
- [native-codex]
moq-pub-mmtp/src/main.rs:210— the stdin frame reader is cancellation-unsafe inside thisselect!:next_input_eventcallsframing::read_one_frame, which consumes the prefix withreadand then usesread_exact. Ifsession_taskorpublish_namespace_taskcompletes while the stdin future is between those reads,select!drops that future; the already-consumed prefix bytes are lost, and the next reconnect starts at the payload or a later frame. That produces a framing error and exits instead of reconnecting. Keep one persistent frame-read future/state across session attempts, or make input ingestion a dedicated task/channel whose read future is never cancelled; add a test that forces a task completion while a frame is only partially available.
Suggestions (0)
Strengths
- The UDP socket is opened once, preserving the multicast join across relay reconnects.
- Reconnect backoff and streak reset behavior are factored into small, directly tested helpers.
- The PR explicitly expands CI coverage to the
release/**backport lane.
Recommended Action
- Fix the Important issue before merge.
- Re-run the package tests and CI on the pinned-baseline branch.
- Verify reconnect behavior with a fragmented stdin frame and a concurrent session termination.
Reviewer notes — the two questions this change should be interrogated on1. Group IDs restart at 0 on every reconnect. Is that a regression?No — it is bit-for-bit the behavior production already has today, just without the downtime. Each reconnect rebuilds So:
Identical in kind, strictly better in duration. This PR does not introduce a new group-id discontinuity; it removes the outage wrapped around the existing one. Production confirms the router in play is the datagram one, so this is the relevant path: If a reviewer wants group IDs to survive a reconnect, that is a separate and larger change (the counter would have to outlive 2.
|
aca78a0
into
release/pinned-live-edge-21006781
Issue: https://paperclip.blockcast.net/BLO/issues/BLO-26173
Why a backport instead of re-pinning to
mainProduction
blockcastd-moq-pub-mmtp(the live public Solana shred publisher) crash-loops on relay session timeout: it propagates the error out ofmain, exits 1, and relies onrestartPolicy: Always. Most recent occurrence 2026-08-22 —restartCount: 11in 34 minutes, every oneexitCode: 1.The fix already exists on
main(#76 + #78), but production is not onmainand deliberately so.onprem-k8s/production-values.yaml:1221pins the pub/relay pair to moq-rs21006781— "the four-MTU oldest-drop queue bound on the prior production protocol baseline… without the unrelated broad moq-rs upgrade" — authored by Omar Ramadan on 2026-08-06.mainis 165 commits ahead of that pin and moves exactly the surface the pin exists to freeze:moq-transport/src/session/request_id.rsmoq-transport/src/session/draft19.rsmoq-transport/src/session/mod.rsmoq-transport/tests/draft19_wire.rsA mismatched pub reingests the shred outage (
session error: invalid request IDcrashloop). So this PR ports the resilience fix onto the pinned baseline rather than dragging the protocol migration in behind it.Base branch
release/pinned-live-edge-21006781was created at that exact pinned commit so this renders as a reviewable 3-file diff.Scope gate
Strictly
moq-pub-mmtp/**. The relay image is untouched, so the pub/relay pair stays matched.What changed
--reconnect-backoff-min-ms500 →--reconnect-backoff-max-ms30000) instead of exiting.--mmtp-input=udpthis is what keeps the SSM(S,G)join alive — the join is a property of the socket, not of the moq-transport session. (AC BLO-8047 §B1: raw-passthrough fragmentation contract (stacked on #1) #2.)select!so the source is owned bymainacross reconnects. Ingest stays a single ordered flow, sodatagram.rs's monotonicgroup_idassignment is unchanged and wire output is byte-identical.Deliberately NOT backported
moq_pub_session_reconnect_totalcounter.metricsis not a dependency ofmoq-pub-mmtpat this baseline andmetrics_endpoint.rsdoes not exist here — adding them pulls in the whole observability stack this backport exists to avoid. Reconnects are observable via structuredtask=/attempt=/backoff_ms=log fields instead, which satisfies BLO-26173 AC BLO-8047 §B4: G6 byte-diff vs libmoq — wire formats diverge by design (no code change) #4 ("counter and/or log atinfo").moq-transport/src/serve/subgroup.rsbinary-search change. Pure read-path optimization, unrelated to reconnect, and touchingmoq-transportis precisely what the pinned pair forbids.Verification status — please read
cargo build/testwere not run locally: the agent workspace has a Rust toolchain but no C linker (ccnot found) and no root to install one. This PR is pushed so CI performs the first real compile. Static checks that were done:cargo fmt --checkclean;bail!import retained (still used at 5 sites);connect()confirmed to returnanyhow::Resultso nouseless_conversion.Do not merge on a red or absent check. The tests added here (
backoff_duration_doubles_and_caps,healthy_session_resets_the_reconnect_streak,reconnect_sleeps_the_current_attempt_then_increments, the two stdinnext_input_eventcases) are the unit-level gate; the 48hrestartCount-flat criterion is only verifiable post-deploy.Not in this PR
The production re-pin. That is kkroo's call once an image exists from this branch — see BLO-26173 and onprem-k8s#2560.