Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Whenever you message another process and need a reply there is a risk of deadlock. I didn't find any primtives in OTP for handling this, you have to structure your actor interaction to avoid it. You can't just have a little bit of shared memory.

The actor model doesn't really offer any benefit over other models while bringing significant downsides. Plus, there are major downsides to using an unpopular platform like Erlang/BEAM.



> Whenever you message another process and need a reply there is a risk of deadlock.

There are edge cases, sure, but I have yet to encounter a deadlock after 7 years of professional work with Elixir.

> I didn't find any primtives in OTP for handling this

See `GenServer.call/2`. This covers 99% of call/return patters in distributed systems. I take it you haven’t written much (any?) Elixir because you would have found this function.

> The actor model doesn't really offer any benefit over other models while bringing significant downsides.

Actors are a way better abstraction for pretty much any application-level code I can think of. I say this having written Go, Rust, and Elixir. What downsides are you talking about specifically?

> Plus, there are major downsides to using an unpopular platform like Erlang/BEAM.

The BEAM is popular. At least 3 different serious languages use it. What down sites are you waving your hands up here?


>> Plus, there are major downsides to using an unpopular platform like Erlang/BEAM.

> The BEAM is popular.

At this point I'd say it's more popular than ZIO, an effects/io/concurrency library basically implementing its own language and threading model on top of Scala


Unpopular as opposed to what, Scala/Zio?


Beam languages have a different concurrency model than what you’re used to in JVM world. I suggest that you try some of them in a real project (even without actors).


OTP typically handles this with timeouts and then restarts when timeouts occur. Not to say it can't happen, but there are strategies.


sometimes people think they know better and want to reinvent the wheel


```

    Whenever you message another process and need a reply 
    there is a risk of deadlock.
```

can you please explain how there is risk of deadlock here ? thanks !


Risk of deadlock is real if you have processes calling each-other in a cyclic way. e.g. process A sends GenServer call to process B, that then sends a GenServer call to process A to in order to handle the original call. However, process A is busy waiting on B to reply to it's initial call.

This is rarely a problem in practice however.


receive takes a timeout. A would crash/hit the timeout and deal with the problem.


Yes, agreed, hence rarely a problem in practice ;)


Wouldn't you just `cast` instead of `call` if you thought this was going to be an issue?


you are not blocked on response right ?


You can cast or call ( non blocking, or blocking) you can do either.


how would you get a deadlock with non-blocking requests ?


I never said you did ?


not you per-se no.

but this whole thread started with deadlock on remote calls, and i was curious about how that could be with async calls.


Oh, I think that the mailbox access a process has can wont block unless its full, in which case the message will be dropped.

I think you can check message_queue_len using erlang:process_info/2 to find the mailbox size and simply back off, or fail noisly.


Avoiding exactly that is why erlang gives you genserver.


> Whenever you message another process and need a reply there is a risk of deadlock. I didn't find any primtives in OTP for handling this, you have to structure your actor interaction to avoid it. You can't just have a little bit of shared memory.

I see a lot of people that dislike Erlang that run into this or are afraid of running into it.

Receive with timeout is certainly a primative to handle this, and there's things like process aliases so that late responses can be more easily ignored. But, there's nothing structural preventing it. It's just that when you do it, it hurts, so hopefully you stop doing it.

But when you're in a situation whete you've setup circular messaging, it's very easy to identify. You see a process (A) that has is making no progress and/or has a large mailbox. You see the stack trace shows it is messaging process B, you inspect process B to see why it didn't respond and see it is messaging process A and there you go. Or you look at process A's mailbox and see that it has requests related to the request it's already trying to fill.

> The actor model doesn't really offer any benefit over other models while bringing significant downsides.

I find the actor model makes many types of request/response for persistent state to become very simple. A process owns the state, and application level read/write messages are sent to that process. The mailbox serializes access to provide an ordering and to make changes atomic. You certainly have choose your messages; if you have a counter and do a get and a set message, you will lose updates; you need to do increment/decrement/add kind of messages; same thing for larger/more complex datastructures. Linked datastructures are still tricky.

It's also super nice for request/response connection handling. Just write straight line code that reads from the connection when it needs a request and writes to it when the response has been computed. When you need to be asynchronous, it's more complex, but not so terrible. The process becomes a loop waiting for a fully formed request from the client (which then gets sent off to a request processor) or a reply from a processor (which then gets sent to the client) ... Java's Loom green threads will likely socialize this further than Erlang has.

This kind of feeds into your other question, but IMHO, Actors let you focus on a process owning state and the messages it receives and how they change the state. You don't need to model for the computer where those messages come from, that's emergent system behavior. BEAM doesn't need to know, and doesn't ask, in part because as a changable distributed system, there's not really a way to know. What sends what to who is essential complexity of the system, but the system doesn't need to know about it. If you had a full accounting of how messages are sent, then you potentially find message loops, but ... it's easier to just find and fix any loops at runtime. (hotloading code can make it very low cost to deploy changes, which makes it more reasonable to spend less effort finding issues before release)

> Plus, there are major downsides to using an unpopular platform like Erlang/BEAM.

Sure, you have to do more work yourself. On the otherhand, relying on the work of others isn't always less work than doing it yourself. There are at least a few Erlang consulting shops you can go to if you really need something and are unwilling or unable to do it. You can't find a lot of answers on stackexchange, but you also don't find a lot of wrong or outdated answers either.


I have no idea how you'd solve this even if you were not using have the actor model, if you had functions that triggered a 'functional ring of functions', you'd have the same problem.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: