Rails ApplicationCable and Channels best practice?

604 Views Asked by At

I am currently using Rails to prepare backed for reacting and react-native applications. The token is used for authentication.

I've got two features in my application, which require WebSockets:

  • Chat system(one chat open at a time)
  • Notification system

I see two ways to implement WebSockets:

Two separate channels

First channel: NotificationChannel to which I subscribe when client opens application and then I broadcast sth if it is required

Second channel: ChatChannel to which I subscribe when I open a chat, the code would look as follows:

class ChatChannel < ApplicationCable::Channel
  def subscribed
    authorize_chat_room_id(params[:chat_room_id]) #
    stream_from "chat_channel_#{params[:chat_room_id]}"
  end

  # here methods for receiving data from client
  # maybe some other methods for tracking if sb is typing and so on
  # private authorize_chat_room_id method
end

In this scenario user is subscribed maximally to 2 channels and authorization is performed only once when subscribing to chat.

In this approach, I authorize only once the user, when he subscribes to channel, however, I don't know how having 2 channels opened instead of 1, affects performance on the server.

One channel

The single channel would be called: PersonalChannel, and would take care of all information flow, the code would look sth like this:

  def subscribed
    stream_from "personal_channel_#{current_user.id}"
  end

  def send_message(data)
    if authorize_chat_room_id(data['chat_room_id'])
      #create message
    end
  end

  def start_typing(data)
    if authorize_chat_room_id(data['chat_room_id'])
      # some broadcast
    end
  end

And when I would broadcast, then I would send in the payload for example type(ex. 'MESSAGE_SEND'), and based on that, react client would print the message if chat would be open.

The advantage of this approach is that when sending the message I can broadcast only to one user and I won't get back message from the channel. But in this case, I have to authorize each time performed an action.

My question is which solution would be better in case of performance and what is generally accepted approach(to make Channels very general or to make each Channel take care of single task)?

1

There are 1 best solutions below

0
On BEST ANSWER

On this issue, I would suggest that you consider prioritizing code maintenance concerns over performance.

The concept of channels allows us to decouple publishers from consumers. This simplifies a lot of code and helps with separation of concerns.

For example, a user posting in a chat room wouldn't need to know (nor manage) how many people are subscribed to that chat room, nor who they might be. These concerns are now handled by the pub/sub system rather than the user.

This separation of concerns makes adding or removing chat members much easier (you don't need to updated every user, you only need to update the pub/sub system).

This separation also prevents multiple copies of the data from being stored (the pub/sub system holds the only valid copy).

In the long run, by consolidating all the data into a single PersonalChannel, you'll be reintroducing the coupling between publishers and consumers - this will increase complexity and make maintenance mode difficult over time.

In the chatroom example, each user will need to grab a copy of all the room's members and send each message to all users. This increases the number of copies (of the member's list) as well as introduces synchronization concerns.

The advantage of this approach is that when sending the message I can broadcast only to one user and I won't get back message from the channel. But in this case, I have to authorize each time performed an action.

This advantage is actually something you can easily achieve using the first approach by subscribing each user to a personal channel as well as the other channels, subscribing to three channels instead of two.