Process-Based Concurrency: Why Beam and OTP Keep Being Right

(variantsystems.io)

68 points | by linkdd 9 hours ago ago

34 comments

  • epicepicurean 28 minutes ago ago

    A rewrite of a stateful application written in python with postgres would be more illustrative of how you're solving the same problems but better. Do BEAM applications not use an actual databse? How is crash tolerance guaranteed? In a typical application I'd write crash tolerance would be handled by the DB. So would transactionality. Without it, one would have to persist each message to disk and be forced to make every action idempotent. The former sounds like a lot of performance overhead, the latter like a lot of programming effort overhead. I assume these problems are solved, but the article doesn't demonstrate the solutions.

  • joshsegall 8 hours ago ago

    I think the practitioner angle is what makes interesting. Too many BEAM advocacy posts are theoretical.

    I would push back on the "shared state with locks vs isolated state with message passing" framing. Both approaches model concurrency as execution that needs coordination. Switching from locks to mailboxes changes the syntax of failure, not the structure. A mailbox is still a shared mutable queue between sender and receiver, and actors still deadlock through circular messages.

    • IsTom 5 hours ago ago

      > actors still deadlock through circular messages

      I've rarely seen naked sends/receives in Erlang, you mostly go through OTP behaviors. And if you happen to use them and get stuck (without "after" clause), the fact you can just attach a console to a running system and inspect processes' states makes it much easier to debug.

    • stingraycharles 6 hours ago ago

      Stateless vs stateful concurrency management is very different, though; I can roll back / replay a mail box, while this isn’t possible with shared locks. It’s a much cleaner architecture in general if you want to scale out, but it has more overhead.

    • cess11 an hour ago ago

      With OTP you can trivially decide whether you want your sender to block or not, and how you do your decoupling if you decide it shouldn't.

      In practice you'll likely push stuff through Oban, Phoenix PubSub or some other convenience library that gives you some opinions and best practices. It really lowers the bar for building concurrent systems.

  • baud9600 6 hours ago ago

    Very interesting. Reading this made me think of occam on the transputer: concurrent lightweight processes, message passing, dedicated memory! I spent some happy years in that world. Perhaps I should look at BEAM and see what work comes along?

    • karmakaze 21 minutes ago ago

      Likewise. You should read about the Cerebras WSE configurable colour channel mesh.

  • mrngm 5 hours ago ago

    Related thread from 11 days ago: https://news.ycombinator.com/item?id=47067395 "What years of production-grade concurrency teaches us about building AI agents", 144 points, 51 comments.

  • loloquwowndueo an hour ago ago

    I really tried reading through this but couldn’t - it’s AI-written so it’s like trying to chew cardboard. I gave up after like 3 paragraphs.

  • EdNutting 4 hours ago ago

    How closely is BEAM/OTP related to the foundational work on CSP (and the implementation in Occam/Transputer way back when…)?

    • lou1306 4 hours ago ago

      Good question! It's a bit of a stretch. BEAM has mailboxes, non-blocking sends, and asynchronous handling of messages, whereas the original CSP is based on blocking sends and symmetric channels. Symmetric means you have no real difference between sends and receives: two processes synchrnoise when they are willing to send the same data on the same channel. (A "receive" is just a nondeterministic action where you are willing to send anything on a channel).

      Occam added types to channels and distinguished sends/receives, which is the design also inherited by Go.

      In principle you can emulate a mailbox/message queue in CSP by a sequence of processes, one per queue slot, but accounting for BEAM's weak-ish ordering guarantees might be complicated (I suppose you should allow queue slots to swap messages under specific conditions).

  • emperorz0 4 hours ago ago

    Zero-sharing message passing is known. But what about shared state? Given the majority of systems manage shared access to arbitrarily constrained shared state or shared resources, I'd be interested to see how this should be handled without just saying "database". Maybe another article?

    • sriram_malhar 4 hours ago ago

      One process is made the logical goto for all operations on that data. The process identity is logically the identity of the shared state.

      In other words, it is exactly a database, albeit an in-memory one.

  • rapsey 7 hours ago ago

    > Backpressure is built in. If a process receives messages faster than it can handle them, the mailbox grows. This is visible and monitorable. You can inspect any process’s mailbox length, set up alerts, and make architectural decisions about it. Contrast this with thread-based systems where overload manifests as increasing latency, deadlocks, or OOM crashes — symptoms that are harder to diagnose and attribute.

    Sorry but this is wrong. This is no kind of backpressure as any experienced erlang developer will tell you: properly doing backpressure is a massive pain in erlang. By default your system is almost guaranteed to break in random places under pressure that you are surprised by.

    • Twisol 7 hours ago ago

      Yes, this is missing the "pressure" part of "backpressure", where the recipient is able to signal to the producer that they should slow down or stop producing messages. Observability is useful, sure, but it's not the same as backpressure.

      • IsTom 5 hours ago ago

        Sending message to a process has a cost (for purposes of preemption) relative to the current size of receiver's mailbox, so the sender will get preempted earlier. This isn't perfect, but it is something.

    • librasteve 7 hours ago ago

      Occam (1982 ish) shared most of BEAMs ideas, but strongly enforced synchronous message passing on both channel output and input … so back pressure was just there in all code. The advantage was that most deadlock conditions were placed in the category of “if it can lock, then it will lock” which meant that debugging done at small scale would preemptively resolve issues before scaling up process / processor count.

      • baud9600 6 hours ago ago

        Once you were familiar with occam you could see deadlocks in code very quickly. It was a productive way to build scaled concurrent systems. At the time we laughed at the idea of using C for the same task

        • librasteve 6 hours ago ago

          I spreadsheeted out how many T424 die per Apple M2 (TSMC 3nm process) - that's 400,000 CPUs (about a 600x600 grid) at say 1GIPs each - so 400 PIPS per M2 die size. Thats for 32 bit integer math - Inmos also had a 16 bit datapath, but these days you would probably up the RAM per CPU (8k, 16k?) and stick with 32-bit datapath, but add 8-,16-bit FP support. Happy to help with any VC pitches!

          • EdNutting 4 hours ago ago

            David May and his various PhD students over the years have retried this pitch repeatedly. And Graphcore had a related architecture. Unfortunately, while it’s great in theory, in practice the performance overall is miles off existing systems running existing code. There is no commercially feasible way that we’ve yet found to build a software ecosystem where all-new code has to be written just for this special theoretically-better processor. As a result, the business proposal dies before it even gets off the ground.

            (I was one of David’s students; and I’ve founded/run a processor design startup raised £4m in 2023 and went bust last year based on a different idea with a much stronger software story.)

            • librasteve 2 hours ago ago

              Yes David is the man and afaict has made a decent fist of Xmos (from afar). My current wild-assed hope for this to come to some kind of fruition would be on NVidia realising this opportunity (threat?), making a set of CUDA libraries and the CUDA boys going to town with Occam-like abstractions at the system level and just their regular AI workloads as the application. No doubt he has tried to pitch this to Jensen and Keller.

    • mnsc 5 hours ago ago

      I wonder how much the roots of erlang is showing now? Telephone calls had a very specific "natural" profile. High but bounded concurrency (number of persons alive), long process lifetime (1 min - hours), few state changes/messages per process (I know nothing of the actual protocol). I could imagine that the agentic scenario matches this somewhat where other scenarios, eg HFT, would be have a totally different profile making beam a bad choice. But then again, that's just the typical right-tool-for-the-job challenge.

  • gethly 6 hours ago ago

    Go is good enough.

  • rustyhancock 4 hours ago ago

    I love the idea of Erlang (and by association Elixir), OTP, BEAM...

    In practice? Urgh.

    The live is all so cerebral and theoretical and I'm certain the right people know how to implement it for the right tasks in the right way and it screams along.

    But as yet no one has been able to give me an incling of how it would work well for me.

    I read learn you some Erlang for great good quite a while back and loved the idea. But it just never comes together for me in practice. Perhaps I'm simply in the wrong domain for it.

    What I really needed was a mentor and existing project to contribute to at work. But it's impossible to get hold of either in the areas I'm in.

    • cess11 an hour ago ago

      You could do the introduction to Mix and OTP that the Elixir team provides, https://hexdocs.pm/elixir/introduction-to-mix.html .

      Erlang is weird, it helps if you have some Lisp and Prolog background, but for a while it might get in the way of learning how OTP works.

  • socketcluster 6 hours ago ago

    The Node.js community had figured this out long before BEAM or even Elixir existed.

    People tried to introduce threads to Node.js but there was push-back for the very reasons mentioned in this article and so we never got threads.

    The JavaScript languages communities watch, nod, and go back to work.

    • pentacent_hq 6 hours ago ago

      > The Node.js community had figured this out long before BEAM or even Elixir existed.

      Work on the BEAM started in the 1990s, over ten years before the first release of Node in 2009.

      • masklinn 6 hours ago ago

        And BEAM was the reimplementation of the Erlang runtime, the actual model is part of the language semantics which was pretty stable by the late 80s, just with a Prolog runtime way too slow for production use.

    • hlieberman 5 hours ago ago

      Forget Node.js; _Javascript_ hadn't even been invented yet when Erlang and BEAM first debuted.

    • leoc 6 hours ago ago

      You may be thinking of some recent round of publicity for BEAM, but BEAM is a bit older than JavaScript.

      • socketcluster 4 hours ago ago

        Haha. I guess the BEAM people can nod down at me with contempt and I nod down at the Elixir folks.

        • seanclayton 2 hours ago ago

          at least you're doubling down on your ignorance!

    • xtoilette 6 hours ago ago

      BEAM predates node js

    • worthless-trash 5 hours ago ago

      I think the author is trying to be clever to parody what was written in tfa.