diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md index b1408496a0..fbe9863af7 100644 --- a/actioncable/CHANGELOG.md +++ b/actioncable/CHANGELOG.md @@ -1,3 +1,12 @@ +* Hash long stream identifiers when using Postgres adapter. + + PostgreSQL has a limit on identifiers length (63 chars, [docs](https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS)). + Provided fix minifies identifiers longer than 63 chars by hashing them with SHA1. + + Fixes #28751. + + *Vladimir Dementyev* + * ActionCable's `redis` adapter allows for other common redis-rb options (`host`, `port`, `db`, `password`) in cable.yml. Previously, it accepts only a [redis:// url](https://www.iana.org/assignments/uri-schemes/prov/redis) as an option. diff --git a/actioncable/lib/action_cable/subscription_adapter/postgresql.rb b/actioncable/lib/action_cable/subscription_adapter/postgresql.rb index bdab5205ec..487564c46c 100644 --- a/actioncable/lib/action_cable/subscription_adapter/postgresql.rb +++ b/actioncable/lib/action_cable/subscription_adapter/postgresql.rb @@ -1,6 +1,7 @@ gem "pg", "~> 0.18" require "pg" require "thread" +require "digest/sha1" module ActionCable module SubscriptionAdapter @@ -12,16 +13,16 @@ module ActionCable def broadcast(channel, payload) with_connection do |pg_conn| - pg_conn.exec("NOTIFY #{pg_conn.escape_identifier(channel)}, '#{pg_conn.escape_string(payload)}'") + pg_conn.exec("NOTIFY #{pg_conn.escape_identifier(channel_identifier(channel))}, '#{pg_conn.escape_string(payload)}'") end end def subscribe(channel, callback, success_callback = nil) - listener.add_subscriber(channel, callback, success_callback) + listener.add_subscriber(channel_identifier(channel), callback, success_callback) end def unsubscribe(channel, callback) - listener.remove_subscriber(channel, callback) + listener.remove_subscriber(channel_identifier(channel), callback) end def shutdown @@ -41,6 +42,10 @@ module ActionCable end private + def channel_identifier(channel) + channel.size > 63 ? Digest::SHA1.hexdigest(channel) : channel + end + def listener @listener || @server.mutex.synchronize { @listener ||= Listener.new(self, @server.event_loop) } end diff --git a/actioncable/test/subscription_adapter/common.rb b/actioncable/test/subscription_adapter/common.rb index 3aa88c2caa..80baf2f771 100644 --- a/actioncable/test/subscription_adapter/common.rb +++ b/actioncable/test/subscription_adapter/common.rb @@ -112,4 +112,18 @@ module CommonSubscriptionAdapterTest assert_equal "two", queue.pop end end + + def test_long_identifiers + channel_1 = "a" * 100 + "1" + channel_2 = "a" * 100 + "2" + subscribe_as_queue(channel_1) do |queue| + subscribe_as_queue(channel_2) do |queue_2| + @tx_adapter.broadcast(channel_1, "apples") + @tx_adapter.broadcast(channel_2, "oranges") + + assert_equal "apples", queue.pop + assert_equal "oranges", queue_2.pop + end + end + end end