D4no0

D4no0

I think this discussion should be done with OTP team, because I doubt that elixir will introduce changes to genserver that are incompatible with the erlang version.

0
Post #11
adw632

adw632

Why does the process not exist?

Why is registry_id(room_id) still have a dead process id?

Of course a process can die at any point, and you can crash calling it, but perhaps you need the holy trinity of a Supervisor, DynamicSupervisor and Registry, managing and restarting the rooms?

1
Post #12
mat-hek

mat-hek

Membrane Core Team

Yeah, I appreciate you trying to find the reasoning behind the original decision. Just wanted to make it clear that the way it is designed just doesn’t work in some cases and you have to hack it one way or another. Probably by wrapping with try-catch.

0
Post #13
adw632

adw632

It’s actually to be logically consistent by default across a range of possibilities such as:

  • If the called process does not exist you get an exception on the caller.

  • If the called process exists and crashes as the message is in flight you get an exception on the caller.

  • If the called process exists and the node hosting the process crashes as the message is in flight you get an execption on the caller.

  • If the called process exists and the process crashes after the message has been added to the process message queue but not yet processed, you get an execption on the caller.

  • If the called process exists and the node hosting the process crashes after the message has been added to the process message queue but not yet processed, you get an execption on the caller.

  • If the called process exists and the message is delivered and the processes begins processing the call and then crashes you get an exception on the caller.

  • If the called process exists and is delivered and then the node crashes as the message is being processed you get an exception on the caller.

  • If the called process exists and is delivered and the process does not handle that call, you get an exception on the caller.

0
Post #14
mickel8

mickel8 OP

Membrane Core Team

Unless this discussion is aimed at elixir 2.0 I’m not sure what this topic is meant to get to.

Yeah, yeah, I know :wink: I just couldn’t find any official guidelines so I am just trying to understand how I should design my API. I am also curious about your points of view :slight_smile: I know that call was designed on purpose to raise when a process no longer exists so just trying to get the reasoning behind it.

Also, moving this to the Erlang forum might be a good option as that’s not Elixir thing

Why does the process not exist?

Why is registry_id(room_id) still have a dead process id?

We had a bug where a room could crash under some circumstances. After fixing the bug, a room is no longer expected to crash so we could keep our code and assume that there won’t be further crashes and GenServer.call always succeeds but this situation made me think on how our API should be designed and whether we should be prepared for a process to crash at any time or assume that it is not expected to crash.

The bigger context is that we execute GET request to get all room states. It happened to us that one of rooms crashed in the meantime and the whole request crashed returning 500 error code. If a user re-request all room states it will get correct response so maybe that’s how the system should work and as I said we should keep our code without wrapping it into try/catch.

Another problem is that you don’t know how Room.get_state is implemented. In case of HTTP requests it’s pretty easy as Phoenix handles every single request in a separate process but imagine you are calling this API on your own. You don’t know whether this function can raise or not. If you knew, you could wrap it in try/catch, spawn a separate Task for it (but with async_no_link) or just call directly :thinking:

1
Post #15
lud

lud

I have been there, and it’s very bad. You will go against the Erlang philosophy of letting things crash sometimes, and will have to handle an infinite amount of possible errors at every layer of the application, in every module, etc. Really bad.

The hard part is to accept that yeah, stuff goes wrong sometimes, and there is not much you can do about it. I see two cases, 1. the code will fail 50% of the time. In that case isolate in processes and/or use try/rescue/catch. 2. the code is not expected to fail, failures are exceptional. Then do nothing, let something else retry, it can be the frontend, an Oban job, a Task… just let your supervisor handle that and you can focus on implementing features.

4
Post #16
adw632

adw632

Yes any code can crash because of a missing process, a dead node or a code error such as divide by zero. That’s ok, you either deal with it at design time because you expect it and know EXACTLY how to handle it, or you don’t really know about the possibility or how to handle something unexpected so the right thing to do is to “let it crash” and defer to policy decided in the layer(s) above to recover e.g. through the supervision tree.

In Elixir our code can be highly concurrent, perhaps down deep in your library code is using Task.async_stream to process a stream of items concurrently. Any error that occurs in the spawned task (e.g. due to bad data your function doesn’t handle) should crash the calling process, because you really don’t expect it to fail.

If you are coming from a static typed exception heavy programming language, you are unreasonably forced to declare all possible exceptions and code defensively and exhaustively and try and handle things you can’t reasonably do much about because you don’t have a process model like Elixir to recover and cleanup state when the happy path is violated, such as when a process dies that you expected to be alive. In these other defensive paradigms your code has to do all the work to detect and cleanup, so you write even more code to try and handle all eventualities because you MUST keep the global shared state consistent. Of course, incorrect programs with even more code than the necessary happy path can’t reasonably be expected to correctly handle all eventualities, more code means more bugs.

3
Post #17
adw632

adw632

Exactly.

“Drugs are bad m-kay..”

But it really is a shift in thinking, it’s why Joe Armstrongs thesis was so important, what seems the right thing just leads to more harm than good.

How can we program systems which behave in a reasonable manner in the presence of software errors? This is the central question that I hope to answer in this thesis.
Large systems will probably always be delivered containing a number of errors in the software, nevertheless such systems are expected to behave in a reasonable manner.
To make a reliable system from faulty components places certain requirements on the system.

1
Post #18
D4no0

D4no0

Not to mention that handling errors manually is error-prone :joy: , so this is basically an infinite circle of hell.

1
Post #19
— All posts loaded —