What could be the reason “Unable to find subscription with id” in Rails ActionCable?

I am creating a messaging application using Rails 5.0.0.rc1 + ActionCable + Redis.

I have a single channel ApiChanneland several actions in it. There are several “unicast” actions → ask for something, return something, and “broadcast” the actions → do something, transfer the payload to some connected clients.

From time to time I get an exception RuntimeErrorfrom here: https://github.com/rails/rails/blob/master/actioncable/lib/action_cable/connection/subscriptions.rb#L70 Unable to find subscription with identifier (...) .

What could be the reason for this? In what situation can I get such an exception? I spent quite a lot of time studying the problem (and I will continue to do it), and any tips will be very grateful!

+4
source share
2 answers

It looks like this is due to this issue: https://github.com/rails/rails/issues/25381

Some conditions of the race, when Rails replied that the subscription was created, but in fact it has not yet been completed.

As a workaround, adding a little time out after establishing a subscription, solved the problem.

An additional investigation is needed.

+1
source

, . ActionCable Rails 5 API ( gem 'devise_token_auth), :

():

{"command":"subscribe","identifier":"{\"channel\":\"UnreadChannel\"}"}

():

{"command":"message","identifier":"{\"channel\":\"UnreadChannel\",\"correspondent\":\"client2@example.com\"}","data":"{\"action\":\"process_unread_on_server\"}"}

- ActionCable , - :

/var/lib/gems/2.3.0/gems/actioncable-5.0.1/lib/action_cable/connection/subscriptions.rb:74

def find(data)
  if subscription = subscriptions[data['identifier']]
    subscription
  else
    raise "Unable to find subscription with identifier: #{data['identifier']}"
  end
end

: , . correspondent, - .

, , correspondent :

():

{"command":"message","identifier":"{\"channel\":\"UnreadChannel\"}","data":"{\"correspondent\":\"client2@example.com\",\"action\":\"process_unread_on_server\"}"}

, .

UnreadChannel:

class UnreadChannel < ApplicationCable::Channel
  def subscribed

    if current_user

      unread_chanel_token = signed_token current_user.email

      stream_from "unread_#{unread_chanel_token}_channel"

    else
# http://api.rubyonrails.org/classes/ActionCable/Channel/Base.html#class-ActionCable::Channel::Base-label-Rejecting+subscription+requests
      reject

    end

  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def process_unread_on_server param_message

    correspondent = param_message["correspondent"]

    correspondent_user = User.find_by email: correspondent

    if correspondent_user

      unread_chanel_token = signed_token correspondent

      ActionCable.server.broadcast "unread_#{unread_chanel_token}_channel",
                                   sender_id: current_user.id
    end

  end

end

helper: ( - , Rails cookie )

  def signed_token string1

    token = string1

# http://vesavanska.com/2013/signing-and-encrypting-data-with-tools-built-in-to-rails

    secret_key_base = Rails.application.secrets.secret_key_base

    verifier = ActiveSupport::MessageVerifier.new secret_key_base

    signed_token1 = verifier.generate token

    pos = signed_token1.index('--') + 2

    signed_token1.slice pos..-1

  end  

, SUBSCRIBE, MESSAGE. ( "" ). , subscribed (!) - ( SUBSCRIBE) ( - hook subscribed).

, subscribed hook :

stream_from "unread_#{unread_chanel_token}_channel"

, , unread_chanel_token - "" .

, (, \"channel\":\"UnreadChannel\") "" (, "" ) - ( , ), "", .

- "" - , :

ActionCable.server.broadcast "unread_#{unread_chanel_token}_channel", sender_id: current_user.id

, ?

. ?

+1

All Articles