matthias_toepp

matthias_toepp

How much more crashproof is a server with Elixir/Beam compared to Rust's axum?

I posted the following also in rust’s reddit https://www.reddit.com/r/rust/comments/1f39vhz/how_much_more_crashproof_is_a_server_with/

My understanding is that because elixir uses the beam vm it has per process isolation which is basically used to guarantee that the whole server will not crash on any one request. As I understand it, this is an advantage over what a framework like Rust’s Axum offers, is this correct? How significant of an advantage is this really over presumably just restarting the whole server? (I get that there is a performance tradeoff for gaining this robustness).

Most Liked

LostKobrakai

LostKobrakai

This is comparing apples with oranges. Rust is all about preventing crashes. The BEAM is about allowing for crashes by isolating them as best as possible and letting the system as a whole stay up even in the face of things going sideways in a context, where things going sideways by definition cannot be prevented – distributed systems communicating over networks.

16
Post #2
SichangHe

SichangHe

What you want to know is not whether they are crash-proof, but whether they are fault-tolerant. Let me try to provide some good points as opposed to being a fan of either side.

  • Crash-proof: Tokio isolates panic unwinding within each task; the Erlang VM isolates exception throwing within each process. Neither of them should “crash” unless, e.g. you do panic = abort, or use a NIF that SegFaults, respectively. However, the Erlang VM has a tiny error kernel, so its implementation is more reliable in theory.
  • State corruption: Tokio tasks share memory through synchronization primitives, so state corruption is more likely to mediate from one task to another. Erlang processes do not share memory, and instead pass message around by copying them, so states of Erlang processes are highly isolated. Axum encourages shared state through Clone + Send + Sync + 'static structs (e.g., Arc<Mutex<_>>) by feeding them as magic arguments to request handlers, which can be a large source of state corruption; Erlang only allows the actor model, and encourages processes to not expect other processes to reply. Note that unwind should only cause low-level memory corruption if it is not UnwindSafe.
  • Scheduling: Tokio tasks are cooperatively scheduled, so you should call yield_now for long-running functions and run blocking code (e.g. syscall) on a blocking thread, or else you can suffer from stalls; Erlang processes are preemptively scheduled, so you can do stupid things and get away with it.
  • Debugging: When you do have faults, Erlang provides a superior interactive debugging experience. The Tokio debugger is highly experimental and people just read the logs or traces instead.
  • Mindset: Rust is perfect for problems you understand as a whole, and problematic for situations with many unknown unknowns; Erlang takes the burden off of developers shoulders by applying the end-to-end argument on fault-tolerance. This is why I tend to reach for Axum when I need to serve just a few well-defined requests, a small problem, but reach for Phoenix whenever I feel the problem is larger or I do not fully understand it.


Hope this helps!
Steven Hé (Sīchàng)

jhogberg

jhogberg

Erlang Core Team

Yes and no; those actor frameworks will not help you manage runaway tasks (nor will they grant you much in the way of introspection). In any M:N setup without pre-emption, a job that takes too long will screw up responsiveness, giving you bad latency of unrelated tasks in the best case, and locking things up entirely in the worst.

If we define “crash” as “the system no longer does what it is supposed to do,” Rust et al pushes way more responsibility to avoid them onto the programmer than Elixir.

There’s no free lunch however, and the trade-offs may tilt in Rust’s favor anyway, but I find “language X also has actors with supervision” a bit reductionist. I’ve yet to see any other solution that supports all aspects of resilience (by Hollnagel’s definition) as well as Elixir/Erlang does.

dimitarvp

dimitarvp

To expand a little on Rust: both Axum and ActixWeb are amazing frameworks and I am certain you’ll never have an OS process crash because of them. Still, you the developer can write a non-crash-proof code inside your responders / handlers and then all bets are off.

In Elixir you can straight up try writing this inside your controller function(s):

something = nil
IO.inspect(something.inserted_at)

Which is an error, but nothing will stop, just one request will fail and everything else goes as normal with zero capacity affected.

In Rust, if you write this:

value = None;
stuff = value.unwrap();

Then your entire program might get aborted and die on the spot. Though final disclaimer: I haven’t looked at Axum and ActixWeb in at least a year so maybe double-check; maybe they have added protections against defective code like this since the last time I checked them out.

sbuttgereit

sbuttgereit

Isn’t it true that what we’re really talking about two tools that are really aimed at different problems?

When I look at Rust, I see something that gets me in close contact with the running system with few abstractions: yes there are rules enforced by the borrow checker, rules about mutable state, and lifetimes, etc. But all of that is to protect me from the low level access I have (and all of which I can bypass with unsafe if I need to). Rust is a systems language.

When working with the BEAM VM, aren’t I really working at a different level of abstraction? Moreso than by using this or that library? A lot of that lower level stuff is dealt with by the BEAM and as an Elixir developer I’m largely protected from those details.

At least that’s my hand-wavy understanding.

I think this is why the earlier “Apples and Oranges” comment is really justified. The trade-offs are different, the guarantees are different, and all of those choices where made in efforts to satisfy different needs. Sure, there’s overlap in applicability, but I’d personally take the larger picture and consider what my application needs the most: many web apps don’t benefit from systems level programming… in which case allowing the good people building the BEAM handle those complexities for me is a win (especially considering they’ll likely deal with that level of complexity better than I would as a business systems programmer). However, at a certain level of performance requirement or computational complexity that situation could change (and even then I might write rust on the hot path and allow the BEAM to orchestrate the whole thing). Trying to straight up compare them without a better sense of these larger goals seems misguided.

Anyway… my two bits.

Where Next?

Popular in Questions Top

joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

We're in Beta

About us Mission Statement