Skip to content

ActionCable Redis adapter raises Redis::SubscriptionError: This client is not subscribed when redis_connector returns a ConnectionPool::Wrapper (redis-rb 5.x) #1340

Description

@ksy90101

Steps to reproduce

Configure ActionCable's Redis subscription adapter to use ConnectionPool::Wrapper, a pattern recommended by redis-rb's README for thread safety:

config/initializers/action_cable_redis.rb

require "action_cable/subscription_adapter/redis"

ActionCable::SubscriptionAdapter::Redis.redis_connector = lambda { |redis_config|
ConnectionPool::Wrapper.new(size: Rails.configuration.action_cable.worker_pool_size, timeout: 3) do
Redis.new(redis_config.except(:adapter, :channel_prefix))
end
}
Then have a client subscribe to a channel and disconnect (or call stop_all_streams) so that ActionCable::SubscriptionAdapter::Redis::Listener#remove_channel is invoked from a worker thread.

Expected behavior

The channel should be unsubscribed cleanly without raising.

Actual behavior

Redis::SubscriptionError: This client is not subscribed
redis-5.4.1/lib/redis.rb:185:in 'Redis#_subscription'
redis-5.4.1/lib/redis/commands/pubsub.rb:28:in 'Redis::Commands::Pubsub#unsubscribe'
connection_pool-3.0.2/lib/connection_pool/wrapper.rb:39:in 'block in ConnectionPool::Wrapper#method_missing'
connection_pool-3.0.2/lib/connection_pool.rb:68:in 'block (2 levels) in ConnectionPool#with'
...
actioncable-8.1.3/lib/action_cable/subscription_adapter/redis.rb:151:in 'block (2 levels) in ActionCable::SubscriptionAdapter::Redis::Listener#remove_channel'
actioncable-8.1.3/lib/action_cable/subscription_adapter/redis.rb:179:in 'ActionCable::SubscriptionAdapter::Redis::Listener#when_connected'
actioncable-8.1.3/lib/action_cable/subscription_adapter/redis.rb:150:in 'ActionCable::SubscriptionAdapter::Redis::Listener#remove_channel'
actioncable-8.1.3/lib/action_cable/subscription_adapter/subscriber_map.rb:33:in 'block in ActionCable::SubscriptionAdapter::SubscriberMap#remove_subscriber'
actioncable-8.1.3/lib/action_cable/subscription_adapter/redis.rb:37:in 'ActionCable::SubscriptionAdapter::Redis#unsubscribe'
actioncable-8.1.3/lib/action_cable/channel/streams.rb:139:in 'block in ActionCable::Channel::Streams#stop_all_streams'
...
Root cause analysis
In actioncable/lib/action_cable/subscription_adapter/redis.rb, the redis-rb 5.x branch defines extract_subscribed_client as:

Impact

ActionCable's redis_connector is officially user-configurable, and ConnectionPool::Wrapper is the pattern that the redis-rb README points users toward for "thread safety". Users naturally combine the two and silently get a broken pub/sub on redis-rb 5.x.
The error only triggers on subscription teardown, so it slips through development and bites in production once a client disconnects.
System configuration
Rails / ActionCable: 8.1.3
redis-rb: 5.4.1
connection_pool: 3.0.2
Ruby: 3.4.x

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions