From 2c46ad2c257b1cc15be40375516a1dfd761477c1 Mon Sep 17 00:00:00 2001 From: Sebastian Serth Date: Wed, 13 Sep 2023 04:17:36 +0200 Subject: [PATCH] Wait for subscription confirmation before broadcasting --- app/channels/application_cable/channel.rb | 20 ++++++++++++++++++++ app/channels/synchronized_editor_channel.rb | 12 ++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb index 9aec2305..c02f7466 100644 --- a/app/channels/application_cable/channel.rb +++ b/app/channels/application_cable/channel.rb @@ -2,5 +2,25 @@ module ApplicationCable class Channel < ActionCable::Channel::Base + @streaming_confirmation_callback = -> {} + + def ensure_confirmation_sent + # Currently, we are required to overwrite this ActionCable method. + # Once called and the subscription confirmation is sent, we call the custom callback. + # See https://github.com/rails/rails/issues/25333. + super + @streaming_confirmation_callback.call if subscription_confirmation_sent? + end + + def send_after_streaming_confirmed(&block) + if connection.server.config.pubsub_adapter.to_s == 'ActionCable::SubscriptionAdapter::Async' + # We can send messages immediately if we're using the async adapter. + yield block + else + # We need to wait for the subscription to be confirmed before we can send further messages. + # Otherwise, the client might not be ready to receive them. + @streaming_confirmation_callback = block + end + end end end diff --git a/app/channels/synchronized_editor_channel.rb b/app/channels/synchronized_editor_channel.rb index 6f1835c8..e390b509 100644 --- a/app/channels/synchronized_editor_channel.rb +++ b/app/channels/synchronized_editor_channel.rb @@ -6,11 +6,15 @@ class SynchronizedEditorChannel < ApplicationCable::Channel # We generate a session_id for the user and send it to the client @session_id = SecureRandom.uuid - connection.transmit identifier: @identifier, message: {action: :session_id, session_id: @session_id} - message = create_message('connection_change', 'connected') - Event::SynchronizedEditor.create_for_connection_change(message, current_user, programming_group) - ActionCable.server.broadcast(specific_channel, message) + # We need to wait for the subscription to be confirmed before we can send further messages + send_after_streaming_confirmed do + connection.transmit identifier: @identifier, message: {action: :session_id, session_id: @session_id} + + message = create_message('connection_change', 'connected') + Event::SynchronizedEditor.create_for_connection_change(message, current_user, programming_group) + ActionCable.server.broadcast(specific_channel, message) + end end def unsubscribed