Repository navigation
fix: replay TRANSIENT_LOCAL cache to late-joining subscribers - #2
Conversation
|
Reviewed PR #2 (head feature/transient-local-wait-replay, single commit 0bd2c1f) against base main. Verified every cited file:line via Overall: a focused, correct, well-scoped improvement. The new wait-side TRANSIENT_LOCAL replay genuinely fixes the same-process late-joiner case (component containers / multi-node processes), the lifetime invariant is sound (transient_local_pubs_mutex held across the loop keeps pub pointers alive against rmw_destroy_publisher), there is no lock-order inversion (transient_local_pubs_mutex -> sub_cache_mutex -> cache_mutex is consistent with the publish path), the failing-test-first reproducer is genuine (on main rmw_wait.cpp has no transient_local_pubs reference, so the test fails; on the branch it passes), and CI is green on jazzy/kilted/rolling. Approving on its own merits. One thing to fix before merge (non-blocking but please address the body, or file a follow-up):
Worth noting (pre-existing patterns this PR extends, fine to defer to the scheduled perf/churn PRs):
Simplicity nits (CLAUDE.md sections 2/3, optional):
Test coverage to consider adding: two-path dedup cooperation (publish + wait replay to the same sub across one generation change), >1 TL publisher, and the cross-process path. LGTM to merge as an incremental improvement once the body is corrected. |
The publisher previously only replayed its cached messages inside rmw_publish, so a subscriber that joined after the publisher had gone idle never received the cache. In production this manifested as late-joining subscribers (e.g. `ros2 topic echo`, bridge consumers) silently missing latched topics such as camera_list. Track TRANSIENT_LOCAL publishers on the context and drive cache replay from rmw_wait whenever the registry generation changes. The mutex is held across the loop so rmw_destroy_publisher cannot free a publisher mid-iteration. known_subscriber_paths still deduplicates: whichever path (publish or wait) runs first marks the path, the other no-ops. Tests: - TransientLocalReplayOnWaitNoSubsequentPublish: publisher publishes N messages then goes idle; subscriber joins, calls rmw_wait once, must receive all N cached messages without a fresh publish. - TransientLocalReplayOnWaitDepthOne: same flow with depth=1. Both tests fail on HEAD and pass with this change.
0bd2c1f to
a5b7503
Compare
Closes #1
Summary
The publisher previously only replayed its cached messages inside
rmw_publish, so a subscriber that joined after the publisher had gone idle never received the cache. In production this manifested as late-joining subscribers (ros2 topic echo, bridge consumers, etc.) silently missing latched topics such ascamera_list.This change tracks TRANSIENT_LOCAL publishers on the context and drives cache replay from
rmw_waitwhenever the registry generation changes — the existing publish-side replay still works; the wait-side path covers the idle-publisher case. Both paths cooperate viaknown_subscriber_paths(whichever marks a path first, the other no-ops).Implementation notes
UdsContextgainstransient_local_pubs(+ a mutex). Publishers register on create, deregister on destroy.transient_local_pubs_mutexacross the whole loop sormw_destroy_publisher(which takes the same mutex then deletes) cannot free a publisher mid-iteration.Test plan
TransientLocalReplayOnWaitNoSubsequentPublish— publisher publishes N messages then goes idle; subscriber joins, callsrmw_waitonce, must receive all N cached messages without a fresh publish.TransientLocalReplayOnWaitDepthOne— same flow with depth=1.mainand pass on this branch (verified viagit stash+ rebuild + ctest).ctestsuite: 12/12 pass.