#Help with actors and supervisors

1 messages · Page 1 of 1 (latest)

rich surge
#

I'm having some issues with trying to set up actors and supervisors.

The code is a bit too large for discord message, so here is a gist: https://gist.github.com/mooreryan/f5746df4105072a6bce09d973b0031e4

The error I'm getting is this:

=SUPERVISOR REPORT==== 5-Feb-2026::11:49:08.154546 ===
    supervisor: {<0.83.0>,gleam@otp@static_supervisor}
    errorContext: child_terminated
    reason: <<"oops">>
    offender: [{pid,<0.84.0>},
               {id,0},
               {mfargs,
                   {gleam@otp@static_supervisor,start_child_callback,
                       [#Fun<gleam@[email protected]>]}},
               {restart_type,permanent},
               {significant,false},
               {shutdown,5000},
               {child_type,worker}]

runtime error: panic

callee exited: ProcessDown(//erl(#Ref<0.68767505.3939500033.239374>), //erl(<0.84.0>), Abnormal("oops"))

stacktrace:
  gleam/erlang/process.-perform_call/3-anonymous-1- src/gleam/erlang/process.gleam:626
  gleam_erlang_ffi.select /Users/ryan/Projects/tmp/xx/build/dev/erlang/gleam_erlang/_gleam_artefacts/gleam_erlang_ffi.erl:60
  gleam/erlang/process.perform_call src/gleam/erlang/process.gleam:644
  xx.main src/xx.gleam:56

There were a couple other questions that got similar errors:

But, I wasn't able to figure out what was going wrong with my code from them.

(I have some experience with Elixir, having read Elixir in Action, but have only made toy apps with it, and am not comfortable with OTP.)

Gist

actors & supervisors issue. GitHub Gist: instantly share code, notes, and snippets.

viscid tiger
#

You’re deliberately abnormally exiting

#

What is it you are expecting the behaviour to be?

rich surge
#

I thought that the supervisor would catch abnormal exits (or really I thought any "crashes"). Eg, I tried a panic to see if it would restart

viscid tiger
#

Supervisors don’t catch exits, they restart processes

#

The caller isn’t supervised. It’s the main process so if you panic that then it dies

rich surge
#

yeah I guess I need to go back and reread elixir in action

rich surge
viscid tiger
#

It's not the fact that it's called main

#

The code crashes because it's written to crash, and as it's being run by the main process the program fails

#

What is it you expect to happen?

rich surge
#

I thought the supervisor would restart the process. Like say I called erlang:error(oops) inside a process that was supervised, I thought that the supervisor of that process would say "hey, this processes terminated, I better restart it"

mossy salmon
#
  actor.call(squidward_subject, timeout, fn(subject) {
    GreeterMessage(client: subject, name: "Patrick")
  })
  |> io.println

when this code runs the message sent to eh process causes it to exit, which means it does not reply to the call, which means this process (your main process) crashes

#

a call expects a response

rich surge
#

ohh, so calling call crashes the calling process?

viscid tiger
#
runtime error: panic

callee exited
#

ya

#

It panics because the callee exited

mossy salmon
#

Send a synchronous message and wait for a response from the receiving process.

If a reply is not received within the given timeout then the sender process crashes rather than leaving the processes in an invalid state.

rich surge
#

Ohhh: Panics
This function will panic under the following circumstances:

The callee process exited prior to sending a reply.
The callee process did not send a reply within the permitted amount of time.
The subject is a named subject but no process is registered with that name.
(from the docs)

mossy salmon
#

you send a message to squidward, then squidward exists and doesnt send a reply, then time timeout expires and the caller crashes because it never got a reply

#

so it doesnt matter that the supervisor restarted squidward (it did), your code is waiting for a reply that wont come and then crashes when the timeout runs out

rich surge
#

Interesting. Okay, I think I understand a bit more than before at least. I switched the code around so that the process running all the call functions sends like process.send_exit(pid) or process.kill(pid) and the process i sent those to does get restarted

rich surge
viscid tiger
mossy salmon
#

neat 💕

viscid tiger
#

Same thing just less waiting

rich surge
#

So, if this were a real app, and I used call , and for some reason the process that was the subject of that call crashes and call doesn't receive a reply, then how do I recover from that? Ensure that the caller of call is also in the supervision tree?

mossy salmon
#

your supervision tree is like this

app
|
static supervisor
|         \
spongebob  squidward

if app exits or crashes theres nothing above it to restart it

rich surge
#

so if I put this into an otp application, would that be the right thing to do?

viscid tiger
#

Put what in an OTP application?

#

What would be the right thing to do about what?

rich surge
#

well, I thought in a real app I probably wouldn't have a supervisor called in main. I would set up an otp application and run the erlang shipment (I thought)

viscid tiger
#

Yeah!

rich surge
#

Okay, so ... currently its like this:

app
|
static supervisor
|         \
spongebob  squidward

But that app thingy also needs to be supervised? I'm still trying to understand how to address this:

So, if this were a real app, and I used call , and for some reason the process that was the subject of that call crashes and call doesn't receive a reply, then how do I recover from that? Ensure that the caller of call is also in the supervision tree?

viscid tiger
#

You don't recover, it crashes and those processes would be restarted (and possibly their siblings depending on how your tree is designed)

#

If you use the OTP application system to start your supervision tree the top supervisor of your application is supervised by the application controller

rich surge
#

Thanks for the help y'all! I think this is just beyond my skill level at the moment. I will keep trying to figure out OTP though

plush sinew
#

I think that if you replace the actor.callwith just:

#
process.send(subject, GreeterMessage(..))
process.receive(client_subject, timeout) |> io.println

You will not monitor the remote and your main process will not crash.

plush sinew
#

But yes, you should move your client process(es) under supervisor tree(s) as well.