I am really excited to be incorporating broadcast_render_to/broadcast_render_later_to to render turbo stream partials and seeing it all mostly “just work” across all open browsers.
Even though the partials are very small/simple, I figured I’d try to use the later version to kick these off to a job to see how it performs, and I’m pretty stumped at the moment, because every Turbo::Streams::BroadcastJob job finishes successfully regardless of which queue adapter I use, but the web socket in the browser only actually receives a message if it’s sent via the inline or async adapters. Using solid_queue shows the jobs themselves succeeding almost instantly but no messages ever make it to the browser.
Welp, this was my own damn fault for forgetting ActionCable subscription information has to be stored somewhere if you’re going to use an outside-of-process queue adapter.
Explanataion:
I did a little digging into turbo-rails’ source and I got this far with both :async and :solid_queue looking the same until you look at the state of the ActionCable.server
# app/channels/turbo/streams/broadcasts.rb
def broadcast_stream_to(*streamables, content:)
streamables.flatten!
streamables.compact_blank!
if streamables.present?
ActionCable.server.broadcast stream_name_from(streamables), content
# > ActionCable.server.connections
# :async => [#<ApplicationCable::Connection:0x0000000002cd30>, #<ApplicationCable::Connection:0x0000000002cd58>]
# :solid_queue => []
end
end
So only async has ActionCable subscriptions…… of course it does, because the default ActionCable configuration only has an async subscription adapter configured:
# config/cable.yml
development:
adapter: async
So, lesson learned. If you want to use a real queue adapter for Active Job in development, you need to use a real subscription adapter for Action Cable too!
Huh, this is indeed strange! I wonder if when running on adapters other than inline or async, the job is failing in reality but with an error that gets automatically discarded, given the impression that it went all well, but it did nothing? ActiveJob::DeserializationError comes to mind, as it’s discarded by default