Cannot broadcast messages to different topics but only from one channel

I’m working with Channels and Endpoint to send messages between the channels and their topics. I recently had to add a new ‘inter-channel’ communication path to remove some data. Basically, an admin will send a message to its own admin channel to cancel the order from employee x, the handle_in will then do a broadcast to employee:x with the message “finish_order”, which already has its handle_in function since the employee can also finish the order itself.

Problem is that I can’t seem to be able to send the message to the employee channel:topic. I’ve even tried hardcoding the employee topic but it just never hits the channel. What feels even more strange is that I’ve been using Endpoint.broadcast! throughout the program and it’s been working perfectly. Employee can send broadcasts to Clients, Clients can send to Employee. Only Admin isn’t able to broadcast and I just ran out of things to try.

# Admin
def handle_in("remove_order_from_driver", %{"client_id" => client_id, "order_id" => order_id, "employee_id" => employee_id}, socket) do

    Logger.info("Admin is removing an order from #{driver_id}")

    Endpoint.broadcast!("employee:#{employee_id}" ,"finished_order", %{client_id: client_id, order_id: order_id})

    {:noreply, socket}

  end
# Employee
def handle_in("finished_order", %{"client_id" => client_id, "order_id" => order_id_client},socket) do
# Logic to end the order, which is already being used by the employee frontend.
{:noreply, socket}

think you are mixing/mismatching string/atom keys in your maps… (“client_id” => value VS client_id: value etc)

so
Endpoint.broadcast!("employee:#{employee_id}" ,"finished_order", %{client_id: client_id, order_id: order_id})

will never be matched to
def handle_in("finished_order", %{"client_id" => client_id, "order_id" => order_id_client},socket) do

so use one or the other…

Thanks for replying. One of the first permutations of code that I tried actually had string keys but I changed it since elsewhere in my (apparently) working code I’m doing it that way. As in atom keys being sent to string key handlers. Now I’m left wondering if other parts of the code are simply not working but it certainly doesn’t seem that way…

Regardless I just substituted this particular instance so they both are string keys and I’m getting the same result. Admin channel does receive the message and shows the log I placed but the Driver channel handler is just not responding…

Is there a way to debug MyApp.Endpoint.broadcast so I can see what’s going on?

bit rusty on the broadcast usage actually…

from a quick look in my own code, Endpoint.broadcast! goes straight to the clients (usually js)… so that is probably why everything else is working just fine…

if you want to intercept the message, you have to use an “intercept” :grinning:
eg add this to the channel code


intercept(["finished_order"])

and then it’s handle_out instead of handle_in… see: https://hexdocs.pm/phoenix/Phoenix.Channel.html#intercept/1

eg:

intercept ["new_msg"]

def handle_out("new_msg", payload, socket) do
  push(socket, "new_msg", Map.merge(payload,
    is_editable: User.can_edit_message?(socket.assigns[:user], payload)
  ))
  {:noreply, socket}
end

handle_out/3 callbacks must return one of:

{:noreply, Socket.t} |
{:noreply, Socket.t, timeout | :hibernate} |
{:stop, reason :: term, Socket.t}

but it seems like you are coupling code and violating seperation of concerns with your code…

can’t you have the logic separated or in it’s own module… and the js client just receives updates/changes on the orders?

Employee channel is mostly sending messages to Client channel directly, I think I made a post about it when I was first working on it. It might be slightly broken it’d be pretty evident if it wasn’t working, since updates to clients depend on those messages being passed correctly. I’m still going to double check just in case.

I’m going to read the intercept docs, that might be useful. I was also thinking about passing regular messages and do a handle_info but I’m not certain how to obtain the PID of a specific channel.

You’re also right, the code really could use a refactoring but for now I need to solve the issue and then I can focus on fixing my own mess :sweat_smile:

I was trying to avoid having to release an update to the JS client but it seems like it’ll be much worse in the end. Based on all that @outlog mentioned, I’m broadcasting the message to the employee topic:subtopic, receiving that on the frontend and having it send its own “finish order” message. I’ve yet to try this but from everything I’ve gathered, it should work and I’ll avoid a mess in my Elixir code.