diff --git a/lib/action_cable/server/base.rb b/lib/action_cable/server/base.rb index 3da7643..f0f7360 100644 --- a/lib/action_cable/server/base.rb +++ b/lib/action_cable/server/base.rb @@ -73,6 +73,8 @@ def disconnect(identifiers) def restart connections.each do |connection| connection.close(reason: ActionCable::INTERNAL[:disconnect_reasons][:server_restart]) + rescue => e + logger&.error "Failed to close connection during restart: [#{e.class} - #{e.message}]: #{e.backtrace.first(5).join(" | ")}" end @mutex.synchronize do diff --git a/test/server/base_test.rb b/test/server/base_test.rb index d46debe..7d17125 100644 --- a/test/server/base_test.rb +++ b/test/server/base_test.rb @@ -15,6 +15,12 @@ def close end end + class RaisingConnection + def close(*) + raise ClosedQueueError, "queue closed" + end + end + test "#restart closes all open connections" do conn = FakeConnection.new @server.add_connection(conn) @@ -35,4 +41,64 @@ def close @server.restart end end + + test "#restart does not propagate exceptions raised by connection#close" do + @server.add_connection(RaisingConnection.new) + + assert_nothing_raised do + @server.restart + end + end + + test "#restart still closes remaining connections when one #close raises" do + @server.add_connection(RaisingConnection.new) + survivor = FakeConnection.new + @server.add_connection(survivor) + + assert_called(survivor, :close) do + @server.restart + end + end + + test "#restart still shuts down worker pool when connection#close raises" do + @server.add_connection(RaisingConnection.new) + + assert_called(@server.worker_pool, :halt) do + @server.restart + end + end + + test "#restart still shuts down pub/sub when connection#close raises" do + @server.add_connection(RaisingConnection.new) + + assert_called(@server.pubsub, :shutdown) do + @server.restart + end + end + + test "#restart is safe to call twice when a connection#close raises" do + @server.add_connection(RaisingConnection.new) + + @server.restart + assert_nothing_raised do + @server.restart + end + end + + test "#restart logs an error including the exception class when connection#close raises" do + @server.add_connection(RaisingConnection.new) + + log = StringIO.new + old_logger = @server.config.logger + @server.config.logger = Logger.new(log) + + begin + @server.restart + + log.rewind + assert_match(/ClosedQueueError.*queue closed/, log.read) + ensure + @server.config.logger = old_logger + end + end end