diff --git a/actioncable/CHANGELOG.md b/actioncable/CHANGELOG.md index b1408496a05de964c09913aa29c0f462fee23e8c..fbe9863af7bd4b527f4d96989ebcf6ba230e95a1 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 bdab5205ec91cc1456b1e3150abbf9e5bf67ec45..487564c46c75c92ec03b7b5f30e4fb3edb3bf3f1 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 @@ def initialize(*) 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 @@ def with_connection(&block) # :nodoc: 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 3aa88c2caa1b0796b4b7a65faf962741f8a96f0d..80baf2f771f1fedf3e89e6f7b19277f3409c0382 100644 --- a/actioncable/test/subscription_adapter/common.rb +++ b/actioncable/test/subscription_adapter/common.rb @@ -112,4 +112,18 @@ def test_channel_filtered_broadcast 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