Show HN: Lunatic – Actor System for Rust/WebAssembly
github.com
github.com
Until now adopting Erlang or Akka meant restricting yourself to a single language ecosystem, but Webassembly maturing and more and more languages gaining support means that you can still use a heterogeneous ecosystem while benefiting from easy sandboxing and isolation, almost instant startup, actor snapshotting/migrations, a unified way of inter-service communication ...
All while avoiding the complexities of grpc, building Docker images, Kubernetes, service meshes, "serverless" frameworks, ...
WASM running in the browser pushes languages to adopt it. Even Microsoft is really investing into dotnet support via Blazor [5].
Also check out wascc [1] [2], which is similar, also written in Rust, quite a bit further along , has some very cool concepts around security and code signing, and supports multi-node systems. (not affiliated)
There is also a spec for distributing WASM via OCI (aka Docker) images, which is useful here. [3]
Some Webassembly proposals like interface types [4] will really help out in this context as well. Sadly wasm has not quite been evolving at the pace I would have hoped for since 1.0. Progress is there, but from the perspective of an enthusiastic early adopter it can seem glacially slow.
[2] https://www.youtube.com/watch?v=vqBtoPJoQOE
[3] https://github.com/solo-io/wasm-image-spec
[4] https://hacks.mozilla.org/2019/08/webassembly-interface-type...
[5] https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor
The BEAM supports at least 20 languages.
I actually think think BEAM gaining the ability to run WASM would be a smart development.
It would make using high performance C/C++/Rust code much easier than the NIF/Ports hassle, with much better sandboxing and scheduling control for the VM.
If they also add WASM actors on top it could expand the interest significantly.
OTP has decades of tuning and improvements that will be hard to match in other projects like this.
I very much agree with this. While Actor model systems have not been commonly encountered in the circles I've run in (we don't need no stinking XML Web Services, we're going to microservices wooooo), this confluence of (Actors + Rust + Wasmtime) seems perfect for the age of increasingly-parallel CPUs as Moore's Law comes in for a landing.
I hope these kinds of efforts start to represent the dawn of a beneficial trend that starts us down the curve of excitement, cargo culting, then depression, then plateau of standard utility.
This trend doesn't have to get as bad as the OO craze of the 90s, but it needs to grow large enough to get people to stop reinforcing the OO model out of baby-duck syndrome or inertia.
I don't think this will happen to be honest. The WASM process still has to run somewhere, it's probably going to be wrapped up into those systems.
Also all of the code written against existing system interfaces for IO etc will not be compatible with the WASM runtime, which means you'll have to run WASM + something else too (and the easiest path is probably going to be to use those "ops" systems you mentioned).
At work I have a Rust web server with basically two parts - What humans see (HTML), and what computers see (API)
The API rarely changes, and I was thinking of splitting them into two services so that I could iterate on the human side without worrying about breaking the API side.
But I don't want to do that because it means extra management overhead in Docker, extra build steps, etc. And the Docker image is unwieldy from having all of Debian packed in it.
So 1 Docker image with 2 Wasm servers in it, is less overhead (of ops) and less risk. I should still use testing to prevent the API breaking, and I still have to figure out a deterministic way to get Wasm images into the server, and measure the performance overhead of Wasm, but it's a tempting option.
I think I have settled on a typed language using async and writing state to disk with SQLite.
The async functions give me much more context than messaging (Golang, Erlang, GRPC etc) as they preserve the function call stack, SQLite is just the "sync" part of a SQL engine so no message passing there, and types give me machine checkable interfaces.
It's going ok.
"Erlang" itself is a single language, but the BEAM/OTP ecosystem isn't usable only from that language (Elixir is fairly widely used, and there are others with less-broad use).
But, even funnier is saying that about Akka: Akka is a JVM library built in Scala and usable from almost any language on the JVM (so, in addition to its "native" Scala you also have Java, Kotlin, Ruby [by way of JRuby], Clojure, and...well, listing languages with JVM implementations that could use Akka could go on virtually forever.)
I think it's kinda underestimation because Blazor seems to be one of the top "implementations" of support for WebAssembly
It would be great if every github readme had a "motivation" section at the top. Why not link it there?
Their documentation section doesn't immediately yield anything that looks like quick reading to understand the motivation or design philosophy. I could not figure out how to navigate https://erlang.org/doc/ to find introductory material.
Do you have any pointers to good reading that would introduce the Actor pattern in Erlang?
The examples, though, remind me more of Go routines and CSP rather than actors. They appear to be passing channels around to communicate which is very similar to but different than a mailbox open to any other process. Is that just a temporary approach? One of the best aspects of BEAM is OTP and supervision trees (linking processes) alongside flexible process naming. I'm not sure how channels would mesh with that. Granted this looks early stage and maybe you're planning on building mailboxes or process links or similar.
Also, you might consider providing support for a binary message passing format like Message Pack or CBOR. Excited to see where Lunatic progresses!
- Actor prioritization
- Actor pausing
- Killing actors at arbitrary points
- Actor serialization / migration
- per Actor GC (internal to them)
- Multiple languages
- Inter-actor/host protected shared memory in addition to message passing
Has the potential to be better than Erlang and much, much better than Go.
what's the overhead of a wasm instance?
4k sound like not that much to me? Imagine a server with 1M concurrent connections, could it need just 4G? (Plus lots of overheard. And without heap)
But the concept of rust-based Actor frameworks (that compile to WASM!?) is very, very tempting.
So, I write code that compiles to WASM, and then pass that code as a process to Lunatic? Is this pattern intended to be a generation beyond microservices/containers, where rather than writing a "service" that gets put into a container and then managed by a container orchestrator, you're creating processes that get managed by a process manager? To get rid of the container overhead? If that's true, isn't it just all turtles because Lunatic will have to run out of containers (and presumably those will have to be managed/orchestrated)?
Lunatic, to me, looks more like a web browser hosting many JavaScript sandboxes. But instead of JavaScript it's wasm, and instead of a web browser, it's only a host process, like node.js. (I don't know if node.js has a concept of running many side-by-side JS actors. Maybe it does)
So you have 1 Unix process hosting many Erlang-like actor processes.
I'm writing a little bit out of my depth, but I think the huge difference is permissions.
Sure, this all might be possible with containers. But Docker, the only container system I've used, is this huge thing. You have to install it, there's a daemon, it hooks into systemd probably, usually you need to be root unless you do everything just right. It requires kernel support for lxc, so I can't run it on the crappy little ARM boards I have to program.
Lunatic, like a web browser hosting JS sandboxes, is _just_ a Unix process. No root, no permissions. I think it will even work on Windows and OS X.
So yeah, if you're talking about web services where you can already manage many Docker containers on a Linux host no-sweat, it might not add much. But if this can be deployed on arbitrary clients with no root and no Linux kernel dependency, it's a very fun toy that must be useful for _something_.
[1] You may find this cool: https://hacks.mozilla.org/2020/02/securing-firefox-with-weba...
Firefox has a C++ font shaping library. Starting in FF 75, they compile the C++ to webasm, then AOT compile the webasm into native code. Resulting in a memory-safe sandbox for C++. There is some overhead, and it looks like a lot of set-up work. They don't give exact percent numbers on runtime overhead.
I don't know if Lunatic is doing JIT or AOT. I'll generously assume that one day they'll do whatever is faster. Part of the point of wasm is to beat JS performance, so I think at least compared to Electron or Node.js (JS sandboxes in a Unix process) it should be competitive.
And there is: https://getakka.net/