#Error when sending a message to an actor managing a mist websocket connection

1 messages · Page 1 of 1 (latest)

narrow copper
#

There are already several similar questions, yet I can’t seem to find any that match my error. Sorry if dupe :,(!

I have the following setup for my mist websockets:

let broadcast = process.new_subject()

...

  let assert Ok(_) =
    fn(req: Request(Connection)) -> Response(ResponseData) {
        ["ws"] -> {
          let self_subject = process.new_subject()
          let selector =
            process.new_selector()
            |> process.selecting(broadcast_subject, function.identity)
            |> process.selecting(self_subject, function.identity)
          let subjects =
            ServerSubjects(self: self_subject, broadcast: broadcast_subject)
          mist.websocket(
            request: req,
            on_init: fn(_conn) {
              #(subjects, Some(selector))
            },
            on_close: fn(_state) { io.println("goodbye!") },
            handler: handle_ws_message,
          )
        }
...

And then, inside my handle_ws_message function:

fn handle_ws_message(
  state: ServerSubjects(msg),
  conn: mist.WebsocketConnection,
  message: mist.WebsocketMessage(ServerSideMessage(msg)),
) {
    mist.Text(_) -> {
        task.async(fn() { process.send(subjects.broadcast, BroadcastTest) })
        actor.continue(state)
    }
...
}

I expect this to send a message to every alive websocket process, but somehow, i get this error instead:

=WARNING REPORT==== 13-Sep-2024::08:46:17.050599 ===
Actor discarding unexpected message: #(//erl(#Ref<0.3152999596.893386755.5847>), Nil)
=WARNING REPORT==== 13-Sep-2024::08:46:17.050703 ===
Actor discarding unexpected message: atom.create_from_string("DOWN")(//erl(#Ref<0.3152999596.893386755.5848>), Process, //erl(<0.113.0>), Normal)

Additional thing I tried: opening several tabs, each with an active websocket connection. The error still only triggers once.

Lastly: Mist (and gleam in general) has been a true joy to work with, thank you so much for making this.

sleek perch
#

That message usually appears because the process receiving the broadcast does not have a selector of the broadcast subject.

#

When I have these kinds of issues I try to reduce the code until I find the problem. Also try to io.debug the broadcast_subject , self_subject and callbacks subjects and see if any of those matches the ones in the warning report.

narrow copper
#

Here is what I get, not exactly sure how to interpret it:

Listening on http://localhost:5001
ServerSubjects(Subject(//erl(<0.104.0>), //erl(#Ref<0.1486330915.3859283970.171522>)), Subject(//erl(<0.10.0>), //erl(#Ref<0.1486330915.3859283970.171215>)))
                                                                    this is the broadcast subject ⬆️

"sending broadcast test"
Subject(//erl(<0.10.0>), //erl(#Ref<0.1486330915.3859283970.171215>))
                               this is where i’m sending to  ⬆️

Error:
=WARNING REPORT==== 13-Sep-2024::10:12:00.208051 ===
Actor discarding unexpected message: #(//erl(#Ref<0.1486330915.3859283970.171568>), Nil)
=WARNING REPORT==== 13-Sep-2024::10:12:00.208215 ===
Actor discarding unexpected message: atom.create_from_string("DOWN")(//erl(#Ref<0.1486330915.3859283970.171569>), Process, //erl(<0.107.0>), Normal)

You’ll notice that none of the IDs in the error match the broadcast subject.

narrow copper
#

More data points:

  • when I remove the task.async(..) part and just directly call process.send, I no longer get the error. But I don’t get any incoming messages either, it just gets lost in the ether.
  • sending to subjects.broadcast or to subjects.self yields the same results.
#

(either an error or nothing, depending on the presence of task.async)

odd tinsel
#

you're setting up the selector and self_subject in the wrong process

#

you need to do it in the init handler of the websocket process

#

you cant select from a subject meant for a different process

narrow copper
#

Ooooh, I see! I think I was really using this too much like a naive send(pid, message), but it seems a bit more intricate than that. Is this also why the behaviour changes whether I use task.async or not?

Here is the solution that works:

           on_init: fn(_conn) {
              let self_subject = process.new_subject()
              let subjects =
                ServerSubjects(self: self_subject, broadcast: broadcast_subject)
              let selector =
                process.new_selector()
                |> process.selecting(broadcast_subject, function.identity)
                |> process.selecting(self_subject, function.identity)
                |> io.debug()
              #(#(app, state, view, subjects), Some(selector))
            },

pub fn handle_ws_message(...) {
  ... 
  mist.Text(incoming) -> {
      io.debug("sending broadcast test")
      process.send(subjects.broadcast, BroadcastTest)
  }
}
#

Thank you so much 🙂

narrow copper
#

Small correction for anyone reading this in the future: broadcast can’t work using this method, because you can’t «listen» to a subject that the current process doesn’t own. At least, that’s what I understood. You need to use a pubsub dispatch process.