Phoenix Socket Check if a channel is active before broadcasting message

Hi Guys,

I just implemented phoenix socket to my project. basically I am broadcasting messages to my clients like this.

WyzerOnboardingWeb.Endpoint.broadcast!("token:#{user.id}", "token_change", %{access_token: WyzerOnboarding.Accounts.get_access_token(user)})

This line of code executes as part of a queue that broadcasts messages to specific clients. however, I would like to check if a channel is currently active first before sending the broadcast to the channel. in this way, I can avoid sending broadcasts to clients that are offline.

Thanks.

What do you mean ‘active’? Every channel is technically active all the time. If nothing is listening it just goes into the abyss very efficiently. You can use presence to determine ‘who’ is listening, but that’s not really necessary if it’s cheap to generate and send as if there are no listeners then it doesn’t do much.

2 Likes

As Overwind alluded, it’s almost always better to simply broadcast and not worry about checking. Broadcasts are cheap and checking first for activity will be expensive and race prone.

3 Likes

If you do want to check, you can use Phoenix.Tracker to track each connected Channel. This provides a way to query for activity before doing the broadcast. I do this in https://github.com/pushex-project/pushex/blob/master/lib/push_ex_web/channels/push_tracker.ex to avoid broadcasting data to non-connected topics. This is because we have a somewhat high “miss rate” of pushes, and our volume is high enough that avoiding the networking is useful.

This could be beneficial if you are doing something like a database or external resource lookup per broadcast.

1 Like

just keep in mind, depending on your consistency requirements, you can miss broadcasts for clients that are recently active. Tracker is eventually consistent, with a default broadcast period of 1.5s, so you you will necessarily miss some broadcasts by checking for activity if you use the tracker approach. This may be fine for some usecases, but not others. One option is you can allow missed messages with an eventually consistent lookup by attaching some data on the broadcast that lets the caller know they are behind, then they hit whatever API they need to find the missed data. That said, unless you have specific usecases like Steve has, broadcasting as fire and forget is going to be much less complex.

1 Like

Absolutely this. The defaults for Phoenix will work for most applications. The approach broadcast takes is designed to get messages to all listeners, which is probably what you want.

I wanted to give the approach, but you should make sure you are okay with the tradeoffs and that the benefits are worth it for you.

I did notice that you have WyzerOnboarding.Accounts.get_access_token(user) in your call. If that’s the reason you don’t want to broadcast, you could actually handle slightly differently. One option for making sure the database is only hit when a Channel is connected is to broadcast the message WITHOUT the token, and then implement a custom handle_out callback that fetches the token and adds it to the payload. handle_out is invoked in the Channel process itself, so by definition it will only run if a Channel is alive. There’s a slight performance cost to handle_out in specific circumstances, but I don’t think that they’d apply to your app given the message you’re broadcasting.

You are right, that makes sense but i guess i would go with the fire and forget approach… the get_access_token is a db call which is inside a line that executes only for some types of users, and basically speaking, it is not the case of millions or thousands of users. considering the slight performance cost of handle_out and using of Phoenix.Tracker with its lower broadcast period might end up in much more expensive task compared to fire and forget. over optimisation usually leads to more problems and larger code base than usual