3 ms·
IMHO the actor model is great until you need to share something across processes, then you have a distributed computing problem inside your program. For develo
by hackingonempty 1y ago
IMHO the actor model is great until you need to share something across processes, then you have a distributed computing problem inside your program.
For developing fault tolerant multicore programs I think I'm better off using a functional effects system with software transactional memory like Scala/ZIO than Gleam/OTP. I can still use the actor model where appropriate. Plus the JVM software ecosystem and runtime observability / battle-testedness is far better than BEAM.
- isodev 1y agoIn Elixir/Gleam/OTP.., the entire program is a collection of progresses which are isolated from each other. Even if you don’t implement the actor pattern, passing state between processes and coordinating is a solved problem. We have primitives like tasks, agents, GenServer, Supervisors etc.
- hackingonempty 1y agoWhenever 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.
- isodev 1y agoBeam 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).
- g8oz 1y agoUnpopular as opposed to what, Scala/Zio?
- signa11 1y ago``` 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 !
- jhgg 1y agoRisk 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.
- oxidant 1y agoreceive takes a timeout. A would crash/hit the timeout and deal with the problem.
- jhgg 1y agoYes, agreed, hence rarely a problem in practice ;)
- signa11 1y agoyou are not blocked on response right ?
- worthless-trash 1y agoYou can cast or call ( non blocking, or blocking) you can do either.
- signa11 1y agohow would you get a deadlock with non-blocking requests ?
- 1y ago
- asa400 1y agoOTP typically handles this with timeouts and then restarts when timeouts occur. Not to say it can't happen, but there are strategies.
- throwawaymaths 1y agosometimes people think they know better and want to reinvent the wheel
- ashton314 1y ago> 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?
- troupo 1y ago>> 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
- throwawaymaths 1y agoAvoiding exactly that is why erlang gives you genserver.
- toast0 1y ago> 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.
- NL807 1y ago>IMHO the actor model is great until you need to share something across processes Incidentally that's what actors are designed for, passing data and being able to mutate their state without use of explicit synchronisation. You either copy or transfer ownership of data from one actor to the next via message passing. Actual sharing should be only done if the data in question is globally immutable.
- seivan 1y ago[dead]
- troad 1y agoThis is a wild take. It's one thing to criticise the BEAM for things it's bad at, it's another to criticise it for the thing it absolutely excels at. The BEAM is built around passing immutable messages between cheap green threads, watched over by resilient supervisor trees. This means no data races (immutability), no hung threads (supervisor trees), no boilerplate (all of this is built in). The performance is surprisingly good, since mandatory immutability permits reference-based optimisations (copying, passing, etc). The BEAM is, in fact, the perfect platform for the exact use case you describe - fault-tolerant, distributed/concurrent systems.
- giancarlostoro 1y agoNot only does it do this all very safely, it uses the absolute most minimal of memory to achieve this. This video from 9 years ago... 1 million people in 1 chatroom, on a server with 40+ cores and 128GB of memory, one single physical server, it used up about 30 GB of memory. Show me a language that can do this efficiently. In under a second it went to a million different connections. The cores didn't spin up to grind hardcore, none of that. Just efficient lightweight processes running. This isn't even on "raw Erlang" its on Elixir -> BEAM which is impressive on its own in my eyes. I would love to see equivalent videos on similar hardware today in other languages. ;) Please by all mean, show me your JVM application that can handle this. I also love that he thinks all those languages that are a decade (and decades) younger than the BEAM which has been in production since 1992 are more production ready and tested. I don't think he realizes the origin story to Erlang whatsoever, it was 100% built to be used in production for a major telecoms provider. https://youtu.be/N4Duii6Yog0 https://youtu.be/N4Duii6Yog0
- giancarlostoro 1y agoI said BEAM has been in prod since 1992 but I meant OTP. BEAM is even older... Erlang goes back to the 80s.
- toast0 1y ago> IMHO the actor model is great until you need to share something across processes, then you have a distributed computing problem inside your program. I'm sorry, but your multicore computer (and most single core computers, too) are a distributed system, regardless of if you acknowledge it. Shared memory concurrency use implicit communicatiom between threads, but it's still communicating processes, you just don't have the same level of control and isolation. That said, of course there are situations where you can have better results with shared memory and there are situations where message passing is better. You can do shared memory in BEAM with ets, and if that doesn't meet your needs, with NIFs ... you likely lose out on some isolation, etc, and it won't always be a good fit. > Plus the JVM software ecosystem and runtime observability / battle-testedness is far better than BEAM. Maybe battle tested (although, I've certainly battle tested BEAM more than I've tested JVM; but I'll grant that many more people have done battle with the JVM)... but BEAM has quite extensive observability. You can see all sorts of stuff about every process, you can add tracing at run time. You can change the code at run time if you need to add more stuff; and runtime code changing is just in the normal setup, it's not exotic (although many people eschew it)
- troupo 1y ago> I think I'm better off using a functional effects system with software transactional memory like Scala/ZIO than Gleam/OTP Translation: I think I'm better off using needlessly complex overengineered monstrosity that takes insane effort to do even the simplest of things, and that still gives me no actual guarantees, than a simple system that has been battle tested over decades of actual hardcore industrial use.
- AlterEgo7 1y agoThis sounds like a "I could not get my head around ZIO/Fuctional Effects ergo it's a needlessly complex overengineered monstrosity" argument. In fact, functional effects provide another elegant solution to problems requiring structured concurrency. You might like it, you might not. But there are many engineers in the Scala community who were bitten by the exact issues of actor deadlocking / difficulty of debugging compared to functional effects that refactored enterprise codebases from Akka (Scala actors) to functional effects. "A simple system that has been battle tested over decades of actual hardcore industrial use". The JVM certainly is not far from that.
- troupo 1y ago> In fact, functional effects provide another elegant solution to problems requiring structured concurrency. There's nothing elegant about ZIO. > But there are many engineers in the Scala community who were bitten by the exact issues of actor deadlocking / difficulty of debugging compared to functional effects Because you can't truly retrofit a proper actor model onto a runtime that doesn't support it, and doesn't have proper tools for them. So they reach for third-party libs that try to implement half of Erlang, often poorly, and get understandably bitten by issues. > "A simple system that has been battle tested over decades of actual hardcore industrial use". The JVM certainly is not far from that. The JVM isn't built around built-in support for concurrency and distributed computing. Erlang's BEAM is. And its built-in primitives and standard library has been around for over three decades.
- t-writescode 1y agoI think it's also worth mentioning - that hasn't been mentioned yet, Erlang (which Gleam is built atop) is ... *the* language that facilitated the nine nines (99.9999999%) uptime; and one of the major components of its ability to do that came with the robust supervising and cross-machine infrastructures that inspired the actor systems later on. This is, quite literally, Erlang's job. It's one job. And it is proven *excellent* at it.
- victorbjorklund 1y agoCan you give an example where you MUST share mutable data that cannot be passed as immutable data structures? The only thing I can think about is hardcore number crunching and yes that should not be written directly in elixir or Erlang or something like that but you could always write NIFs for that.