feat: explicit subscriber durability for latched topics - #2
Merged
Conversation
Add an optional `durability` field to the `subscribe` op so clients can request `transient_local` (or `volatile`) QoS directly, instead of relying on rws auto-matching the publisher's durability at subscribe time. Auto-matching reads `get_publishers_info_by_topic` when the subscription is created. If the latched publisher has not been discovered yet, the subscriber is created volatile and silently misses the publisher's retained sample — a discovery race. A `transient_local` subscriber is QoS-compatible with a transient_local publisher and receives the retained sample whenever they match, regardless of which side started first, with no dependency on discovery timing. - topic_params: add `durability` (default SystemDefault = legacy auto-match) and include it in equality so subscribers with different durability stay distinct. - Connector::subscribe_to_topic: when durability is explicit, apply it directly and skip the publisher-info query; otherwise keep auto-matching. - client_handler: parse the optional `durability` string on subscribe. Tests: - connector_test: deterministic race reproduction via mocked discovery state (volatile when publisher undiscovered without the param; transient_local with the param even when undiscovered; legacy auto-match still works; explicit volatile overrides; params differing only in durability are distinct). - client_handler_test: end-to-end real-ROS coverage — a transient_local subscriber receives a sample latched before it subscribed, while a volatile subscriber matches the same publisher but does not. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
End-to-end test that talks to a running rws_server over a real WebSocket (no linkage to rws C++). A latched rclpy publisher publishes a retained sample before any subscriber connects, then the test asserts a transient_local subscriber receives it while a volatile subscriber does not. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Use single-line docstrings + comments so ament_pep257 (run in CI) passes. - Comment the dependencies and RMW requirement at the top. - Print what is published/received so the behavior is observable manually. - Drop the rclpy.spin thread: the middleware retains the latched sample for late joiners without spinning, which also removes noisy shutdown output. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the explicit subscriber durability field from a top-level "durability"
key to the spec-conformant {"qos": {"durability": "..."}} location. Only
qos.durability is honored; other QoS fields are not yet supported.
Implementation is otherwise unchanged: transient_local / volatile map to the
same DurabilityPolicy, omitted falls back to publisher auto-match. Updates
README, the C++ e2e tests, and the black-box Python test to the new format.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds explicit subscriber QoS durability so clients can opt into latched (transient_local) delivery, fixing a discovery race where a subscriber that connects before the publisher is discovered is created volatile and silently misses the publisher's retained sample.
A
subscribemessage may include aqosobject (per the rosbridge QoS spec):{ "op": "subscribe", "topic": "/latched_topic", "type": "std_msgs/msg/String", "qos": { "durability": "transient_local" } }qos.durability"transient_local""volatile"Only
qos.durabilityis honored; other QoS fields (reliability/deadline/lifespan/best_available) are not yet supported.Why
The previous behavior derived subscriber durability from
get_publishers_info_by_topicat subscribe time. If the publisher wasn't discovered yet, the subscriber was created volatile and missed the latched sample. Letting the client declare durability explicitly skips the discovery query entirely and is race-free.Changes
connector.hpp:durabilityfield ontopic_params; applied directly when set, else legacy auto-match.client_handler.cpp: parseqos.durabilityfrom the subscribe message.README.md: documents theqos.durabilityfield.Tests
connector_test.cpp: deterministic mock tests reproducing the race both ways (with/without explicit durability), asserting discovery is not consulted when durability is explicit.client_handler_test.cpp: real-ROS e2e — transient_local subscriber receives a pre-published latched sample; volatile subscriber provably does not.test_durability.py: black-box test over a real WebSocket againstrws_server(no rws C++ linkage), manually observable.All gtest suites pass; pep257 clean.
🤖 Generated with Claude Code