Skip to content

Transport rework umbrella: params/profiles, wire protocol, QoS (off-season design) #36

Description

@rolker

udp_bridge big-picture transport rework — design / tracking (off-season)

Status: design + tracking issue, not yet actionable. Off-season effort.
Capturing the big picture so it isn't lost; open questions are deliberately
left open
pending more thought. No implementation is committed by this issue.

This is a deliberately large-blast-radius rework spanning three coupled axes the
field has been pushing on: how parameters are organized, additions/changes
to the wire protocol
, and better QoS handling. A lot of it is already
latent in the tracker (#14, #18, #19, #20, #26, #34, #35); part of the goal is to
decide whether those are one coherent rework or several independent ones.

Cross-cutting decision: flag days are acceptable (for now)

This is still a development system, so we do not need wire-protocol
capability negotiation or mixed-version (boat vs operator) interop. Breaking
envelope changes are fine as long as both ends update together.

Unifying concept: per-topic "transport profile"

Today a topic's behavior is scattered across topics_list (allowlist), the
topics map (source/queue_size/period), the global drop_stale_packets,
and implicit QoS. A profile lets a topic declare its semantics once and
absorbs several open issues:

Workstream 1 — parameters / profiles

Settled in discussion:

  • Drop the label indirection. Key topics by the source topic name
    directly; topics_list collapses into the map keys (the keys are the
    allowlist).
  • Boundary rule: identity & routing are per-topic; behavior is profile.
    • per-topic (always): source topic (the key), destination rename
    • profile (shared): delivery class, gate + reorder window, priority, QoS intent
    • either (profile default, per-topic override): queue_size, period
  • Outbound-only stays — it's a deliberate locality principle (each bridge
    config owns only what it sends), not a bug. The real pain is the silent
    failure when a boat-outbound and operator-inbound pair gets edited on only one
    side. Fix = cross-config validation/discoverability (lint two configs:
    "boat advertises X but no peer subscribes" / "operator requests Y but no peer
    advertises"), not restructuring.

Illustrative schema (not final):

profiles:
  realtime-latest:     # heartbeat, video, costmap window
    delivery: latest_wins
    drop_stale: true
    priority: high
    qos: {reliability: best_effort, durability: volatile}
  survey-stream:       # raw sonar, pointclouds — every sample matters
    delivery: every_sample
    drop_stale: false        # never gate these (the 2026-06-29 lesson)
    priority: low
  gridmap-heavy:       # cube tiles / grids
    delivery: latest_wins
    drop_stale: true
    reorder_window_ms: 20
    default_period: 0.5

connections:
  vpn:
    max_bytes_per_second: 1200000
    advertise:                                    # outbound-only, made explicit
      /sensors/m3/cube_bathymetry/coverage_catalog: {profile: gridmap-heavy, queue_size: 2}
      /sensors/m3/cube_bathymetry/coverage_tiles:   {profile: gridmap-heavy, queue_size: 2, period: 0.5}
      /manda_coverage/coverage_swath:               {profile: gridmap-heavy, destination: /operator/manda_coverage/coverage_swath}

Must verify before committing to "topic name as key": how the bridge
currently ingests this config. ROS 2 parameter names don't love /, and the
YAML→param flattening uses .; slashed keys (/sensors/m3/...) may be rejected
or mangled if the loader uses standard ROS param declaration. If it reads the
structure as an opaque map/YAML, it's free. Also grep that nothing else (stats,
diagnostics, logs) keys off the label today.

Workstream 2 — QoS handling

  • Live bug, not just cleanup: incompatible QoS (DURABILITY) -> No messages will be sent on .../cube_bathymetry/coverage_catalog (2026-06-29). The
    bridge carries reliability/durability on MessageInternal but doesn't fully
    resolve durability across the link.
  • Proper durability across a UDP bridge means the sender caches the latched
    sample and replays it to a late/reconnecting subscriber — a small protocol
    feature (a "send me the latched value" path). With flag days OK this is now
    tractable, but it's the biggest single chunk and may deserve its own phase.
  • QoS intent slots naturally into the profile (above).

Workstream 3 — wire protocol

Candidate changes (flag days assumed), roughly increasing scope:

Relationship to existing issues

Rough phasing (not final)

  1. Bench harness (Bench-test harness for field failure modes (resend amp, wedge, rate-limit accuracy, saturation) #18) — makes the rest measurable rather than "seems better"
  2. Version byte
  3. Param/profile model (absorbs Make the stale-packet gate opt-in per latest-wins topic (it's over-broad) #34/Reorder/jitter buffer for out-of-order packets (hold-then-reorder instead of hard-drop) #35/Add per-topic priority/class scheduling to the rate-limiter #19)
  4. QoS durability / latched-replay (own phase, biggest chunk)
  5. Heavier wire changes (send-stamp, topic IDs)

Open questions (intentionally unresolved)


Authored-By: Claude Code Agent
Model: Claude Opus 4.8 (1M context)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions