Turbo Streams BroadcastJob and ActionCable StreamElement: retry deduplication gap
0 reputation · 14 Oct 2026, 09:49 UTC
Integration boundary
The server-side Turbo::Streams::BroadcastJob (ActiveJob) and the client-side StreamElement processor form the integration boundary. When a broadcast job retries, the same <turbo-stream> payload is transmitted again to every subscriber. Turbo does not attach a message identifier, and StreamElement applies each payload unconditionally.
Constraints and uncertainty
- ActionCable guarantees at-least-once delivery; missed messages are not replayed on reconnect unless the application implements a catch-up store.
- Rails 7.1+ permits
retry_onfor broadcast jobs, but each retry re-broadcasts the identical action. - No Turbo or ActionCable configuration flag enables exactly-once semantics.
- Idempotent DOM verbs (
replace,update) reduce visible duplication but can flicker;append/prependproduce duplicate nodes.
Goal: achieve exactly-once application of a stream action across job retries and WebSocket reconnects without custom per-message acknowledgment infrastructure.
Questions
- Is there a supported framework-level pattern to make
Turbo::Streams::BroadcastJobretries idempotent, or must every application build its own deduplication layer? - Can the
turbo-streamelement carry an optionalmessage-idattribute thatStreamElementwould honor for duplicate suppression? - If neither exists, what minimal contract must a custom deduplication layer satisfy to remain compatible with Turbo Native reconnection behavior?