Skip to content

Add remove/unbridge services (remove_subscribe, remove_advertise)#31

Merged
rolker merged 1 commit into
jazzyfrom
feature/issue-30
Jun 15, 2026
Merged

Add remove/unbridge services (remove_subscribe, remove_advertise)#31
rolker merged 1 commit into
jazzyfrom
feature/issue-30

Conversation

@rolker

@rolker rolker commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Add remove/unbridge services: remove_subscribe, remove_advertise

Closes #30. Prereq for marine_control dynamic device control (ADR-0003 D7-dyn, rolker/unh_marine_autonomy#160).

udp_bridge could add a runtime bridge (remote_subscribe / remote_advertise) but not remove one — disconnect meant tearing down the node or silencing a topic with a negative rate period (which left the forwarding in place). This adds real teardown.

Changes

  • RemoteSubscribeInternal.msg: add OPERATION_SUBSCRIBE=0 / OPERATION_UNSUBSCRIBE=1 + uint8 operation (mirrors the existing ConnectionInternal.operation idiom).
  • subscriber_registry.h (new, testable): removeSubscriberConnectionFrom — pure map logic. Drop a (remote, connection) forwarding; drop the remote when it has no connections; drop the source entry (handing back its subscription for off-lock teardown) when it has no remotes. Idempotent.
  • removeSubscriberConnection: runs that under subscribers_mutex_, resets the freed subscription off-lock, then sendBridgeInfo().
  • ~/remove_advertise: local removal of a local→remote push. ~/remove_subscribe: sends OPERATION_UNSUBSCRIBE so the remote drops its push to us; decodeSubscribeRequest branches on operation.
  • callback(): use subscribers_.find() instead of operator[] (forward + stats paths) so an in-flight message during teardown can't re-insert an empty entry that updateLocalSubscriptions would resurrect as a zombie subscription.
  • 7 unit tests for the removal cascade + idempotency. 102 tests pass.

Review (two Deep adversarial passes — protocol/logic + concurrency)

No must-fix defects: key symmetry (add/remove use the identical (source, remote, connection) triple), direction (remove_subscribe targets the remote, remove_advertise is local), erase cascade, lock ordering, and subscription-teardown safety all confirmed sound — the off-lock reset() is safe because an in-flight callback keeps its own executor ref. Review-driven fixes applied: corrected the teardown-rationale comments and the message's wire-compat note.

Wire-format note: appending operation is not backward-compatible across versions. A new receiver deserializing an old-format subscribe throws on the missing byte; decode()'s existing catch-all logs + drops it (recoverable, no crash, not misread). Deploy the bridge fleet together rather than mixing versions across a link.


Authored-By: Claude Code Agent
Model: Claude Fable 5

udp_bridge could add a runtime bridge (remote_subscribe / remote_advertise) but
not remove one. Disconnecting required tearing down the node or silencing a topic
with a negative rate period (left the forwarding in place). This adds real
teardown, needed for marine_control's dynamic device control (ADR-0003 D7-dyn).

- RemoteSubscribeInternal.msg: add OPERATION_SUBSCRIBE=0 / OPERATION_UNSUBSCRIBE=1
  + uint8 operation (mirrors the ConnectionInternal.operation idiom).
- removeSubscriberConnectionFrom (new testable header subscriber_registry.h):
  pure map logic — drop a (remote, connection) forwarding, drop the remote when
  it has no connections, drop the source entry (handing back its subscription for
  off-lock teardown) when it has no remotes. Idempotent.
- removeSubscriberConnection: runs the map logic under subscribers_mutex_, resets
  the freed subscription off-lock (hygiene; an in-flight forwarding callback keeps
  its own executor ref so the reset is always safe), then sendBridgeInfo.
- ~/remove_advertise: local removal of a local->remote push (mirror of
  remote_advertise). ~/remove_subscribe: sends OPERATION_UNSUBSCRIBE so the
  remote drops its push to us (mirror of remote_subscribe); decodeSubscribeRequest
  branches on operation.
- callback(): use subscribers_.find() instead of operator[] (both the forward and
  the stats paths) so an in-flight message during teardown can't re-insert an
  empty entry that updateLocalSubscriptions would resurrect as a zombie sub.
- 7 unit tests for the removal cascade + idempotency. 102 tests pass.

Reviewed by two Deep adversarial passes (protocol/logic + concurrency): logic,
key symmetry, direction, erase cascade, and teardown safety confirmed sound.
Wire-format note: appending `operation` is not backward-compatible across
versions; a new receiver throws on an old-format subscribe and decode()'s
existing catch-all logs+drops it (recoverable). Deploy the fleet together.

Closes #30

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 15, 2026 03:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@rolker rolker merged commit fb5d05a into jazzy Jun 15, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add remove/unbridge services (remove_subscribe, remove_advertise)

2 participants