Hacker News new | past | comments | ask | show | jobs | submit login
Lunatic: Erlang-Inspired Runtime for WebAssembly (github.com/lunatic-solutions)
188 points by cab404 on Sept 6, 2022 | hide | past | favorite | 45 comments



I'm one of the creators of lunatic and it's great to see it on HN again.

We have been working on a Rust web framework that builds on top of lunatic, called submillisecond[0]. It's still in alpha and there is a lot to be figured out. Currently, we are attempting to build something like Phoenix LiveView[1] for it. One of the drawbacks of LiveView is that it requires a permanent connection to the backend, but because lunatic uses WebAssembly on the backend we can actually move part of the backend into the browser to allow for low-latency and offline use cases. Hopefully, you will be able to "just write" rust backend apps, but automatically get rich user experiences with offline support.

A big struggle for us has been the translation of some of the dynamic patterns from Erlang/Elixir to the strict type system of Rust. Also, Rust can't fully understand lunatic's processes, because the language was not designed with a concurrency model in mind where each unit of work gets their own heap memory. So there is some de/serialization going on when pushing data between processes.

As someone else mentioned, lunatic extends WebAssembly with a concurrency model, networking support, permission system, distributed compute, preemptive scheduling. But it's also a whole system for executing Wasm. So you can from your running wasm app load external .wasm files and spawn processes from them with specific constraints (memory/cpu usage, filesystem access, etc.). Someone in our community is building a system that uses submillisecond in combination with a dynamic router that dispatches workloads to external .wasm files. As you can probably tell, I'm super excited about all the possibilities and the future of lunatic.

[0]: https://crates.io/crates/submillisecond

[1]: https://github.com/phoenixframework/phoenix_live_view


Re: "moving the backend into wasm", is there a repo somewhere we can look at?

I presume the bulk of the work there will be ensuring some kind of consistency between the backend database and whatever persistence layer one uses in the frontend (I presume something backed by `indexdb`)?

I ask, because I'm about to work on a full stack rust application (ie, even UI is handled by wasm) with gRPC, and I think I can learn things from your approach.


There are still many problems to be solved. One is the state syncing between backend and frontend. There is nothing public yet, until we decide on the direction that we want to actually take.


Thank you for your post. I am really excited about lunatic.

Do you have any web socket support? Web sockets that are serviced by actors make for a really nice programming model.


Web socket support was added a few days ago[0], but it's still not part of a release. I will probably push out alpha1 tomorrow including it and a few other changes.

[0]: https://github.com/lunatic-solutions/submillisecond/pull/78


Could lunatic one day run on a website? Wouldn't wasmtime itself need to be compiled to wasm for that to be possible?


Yes. We could use the in-browser wasm engine + a polyfill for the host functions. Of course, this would have some limitations. Functionality like UDP would not be supported.


> because lunatic uses WebAssembly on the backend

Isn't one of the main purposes of WebAssembly how it's like... portable/easy to run in different places? If you are in the backend, why not run a backend language like Rust in the first place + not introduce WebAssembly?


There are many other properties that WebAssembly gives us that would be hard (impossible?) to achieve with native Rust. Erlang-like fault tolerance based on a concurrency model where each unit gets their own heap. Fine-grained per request sandboxing. Green thread based async model. Hot reloading, ...


The sentence you extracted that quote from provides one answer to your question: it means they can take part of the backend code and actually execute it in the browser. Like edge functions, but even more so.

Here's the full context of the snippet you're questioning:

> One of the drawbacks of LiveView is that it requires a permanent connection to the backend, but because lunatic uses WebAssembly on the backend we can actually move part of the backend into the browser to allow for low-latency and offline use cases. Hopefully, you will be able to "just write" rust backend apps, but automatically get rich user experiences with offline support.


>All processes running on Lunatic are preemptively scheduled and executed by a work stealing async executor. This gives you the freedom to write simple blocking code, but the runtime is going to make sure it actually never blocks a thread if waiting on I/O.

I'm reading/writing a fair amount of Python and C# at the moment, and the more I see async/await in use, the less I'm convinced by it as a language feature. Whilst the guidance seems reasonable on reading (e.g. [0]), the reality is that it increasingly becomes the norm to find code where most functions/methods are declared as async, and most implementations are littered with await.

So Lunatic's approach resonates. There again, as an Erlang fan, I'm pre-disposed to that concurrency model. So definitely not unbiased.

[0]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-g...


I was working on a thesis about how async by default was at least as much trouble as it's worth, but at the end I realized that to a first order approximation, my 'solution' was essentially asking for a return to Promises. We all know the problems with that architecture, and I have a mountain of my own anecdotes there, so if I can only come up with alternatives that even I hate, I'm mostly keeping my opinions to myself.


This does not use async/await "colored" functions:

> This gives you the freedom to write simple blocking code, but the runtime is going to make sure it actually never blocks a thread if waiting on I/O.

It's more like Go or Ruby non-blocking fibers, where I/O primitives implicitly and automatically "yield" control to the event loop.

I don't know how that works, but I would guess that there's a big lookup table of such operations in the interpreter/compiler.


> This does not use async/await "colored" functions:

Thanks, yes, that's my point. I'm not convinced by async/await as a technique. I'm finding the "non-blocking fibers" approach much preferable in practice.


They both have their own place. If you're writing code where you care about thread identity, like a GUI code or using a native thread for various reasons and you want specific control, async/await gives you that control. If you don't care about thread identity and just care about pure throughput, pre-emptive is less cognitive load up until the work stealing starts to matter.


C#'s async is cooperative multithreading and this is pre-emptive multithreading. Both have different goals and they have their own place.

My expectation is if/when Java's Loom tech gains steam Microsoft will release its own pre-emptive thread pool into C# alongside async/await (again because they're different styles with different goals). Then everyone will be happy, ...right?


>C#'s async is cooperative multithreading and this is pre-emptive multithreading. Both have different goals and they have their own place.

Yes, but that's not really the issue at hand. The issue is that async/await, as a particular _implementation_ of cooperative multi-tasking, has resulted in code bases dominated by async function declarations and bodies filled with awaits. Where, qualitatively, the volume of async/await code outweighs the actual need for concurrent/non-blocking execution.

>My expectation is if/when Java's Loom tech gains steam Microsoft will release its own pre-emptive thread pool into C# alongside async/await (again because they're different styles with different goals). Then everyone will be happy, ...right?

Maybe. That'll depend on whether they can address the viral nature of async. And it won't automatically get rid of the `xxxMethod/xxxMethodAsync` duality that has arisen. It'll also add yet more cognitive overload for developers ("should I use cooperative or pre-emptive multi-tasking here?").

--

EDIT: fixed spelling


Personally I think the whole coloring controversy is overblown. Complaining about it is strange to me. The awaits must be there because that's what makes it cooperative and explicit not implicit and abstracted. It's like complaining about explicit memory management for garbage collection. GC is great but GC can't do things you can do explicitly. And unlike memory management, async/await is safe, just mildly verbose.

Async/await is probably the best at what it does well. You simply can't do the things that involve native thread management (among others) with implicitly scheduled green threads without locks or some other worse paradigm.


From my own experience in the Elixir community watching libraries develop the non-blocking preemptive actor-model concurrency is a good default for most situations. Dropping into things like Rust NIFs has been great for those hands on situations where direct memory control is needed - but more often than not its the exception not the rule.


This is actually much more than a runtime. As for the actual wasm runtime, it uses wasmtime.

What this project provides is what WASI and webassembly in general is currently missing: all the useful stuff like TCP networking, message passing between webassembly modules, filesystem access, transparent node distribution over a network, scheduling on an executor, permission configuration per process.

This is a first for the webassembly space if I am not mistaken. It will probably catch people by surprise who were trying to bring similar stuff to market of wasm on the server. I'm excited to see what lunatic brings on the table!


Would you say it's an actor system for Wasm modules? That sounds great if it is, I can already see use cases.


Kinda, yes. A runtime means something specific for the wasm standard [0] (providing the wasm VM, the store, other WebAssembly objects, provisioning for imports exports of a module and executing them etc).

Lunatic provides this basic (but not trivial; it's complex) functionality through wasmtime. What lunatic offers is lots of functionality on top for wasm modules to actually do stuff. In general wasm modules by themselves, if not in the browser with access to the web APIs, are pretty useless since they are run in a sandbox and have no access to the outside world except for the imported functions you provide.

To be useful outside the browser, they need to do {network,filesystem} IO, which is the goal of the WASI standard [1]: to provide interfaces to external functionality. Lunatic says on the README it gives interfaces for message passing, filesystem, memory, network connections IO, etc.

WASM standards have actually stopped progressing since the Bytecode alliance took over the entire standard. It's very difficult to find who personally actually makes decisions in Bytecode alliance: going through lots of links and documents and github repos I see mostly employees of Fermyon and Fastly. It's important to notice the Bytecode alliance acts as if wasmtime is the only runtime that exists which is not the reality.

As a complete outsider and observer with no stake in the game, it seems like WASM is dead as a technology on the server unless initiatives like Lunatic take it forward with actual implementations instead of really basic product offerings without much functionality, or worse, vague promises, attempts at trademark registrations, registration of all wasm-related terms domains in most big TLDs. There's lots of incompetance, inexperience and/or malice in the WASM world and it's a shame.

At least before it all went to shit, WASM got full support on all browsers and we can enjoy it there!

[0] https://www.w3.org/TR/wasm-core-2/exec/runtime.html [1] https://wasi.dev/


> As a complete outsider and observer with no stake in the game

Here's a PR on a competing Wasm runtime, which someone on your github account authored. You should consider changing your GitHub password or getting a carbon monoxide detector. https://github.com/wasmerio/wasmer/pull/3013


haha! I quit a few weeks ago and now I'm free.


Wow, this looks actually great then!


Recently I have been working on porting a Prolog interpreter to WASI (Trealla). I’ve been searching for a way to get these kinds of useful I/O features in WASI and this looks like a great solution. Is there a C API? I’d love to take a shot at supporting it.


There is no C API, but it would be a great addition. I assume that it would be just a thin wrapper around the host functions and the host functions are fairly good documented (e.g. https://github.com/lunatic-solutions/lunatic/blob/main/crate...), so it shouldn't be too hard to add.


Thank you. Looks very clean, I will try poking at it soon!


Are you aware trealla can already target WASI as a target? (Just fyi in case you weren't)

https://github.com/trealla-prolog/trealla#webassembly-wasi


That was me who ported it :) Currently there’s no support for networking and stuff, would love to fix that.


I happen to be writing a prolog implementation for fun. I've been following the book "The implementation of Prolog" (1993, Princeton University Press). Do you have any suggestions on implementation reading material?


That's super cool. I'm definitely not an expert on the subject but this paper about dif/2 intrigues me: https://arxiv.org/abs/1607.01590 And perhaps Ciao's paper could offer some hints? http://cliplab.org/papers/hermenegildo11:ciao-design-tplp.pd... You're probably already aware of it but Scryer Prolog seems like an interesting place to borrow ideas from. Both Scryer and Trealla have efficient string (list of characters) representations.


HN Comment of the Day!


Haha oops!

Great job btw.


I love the approach of Lunatic. It really fulfils the promise of WASM I think, as a universal general purpose runtime.


As someone perpetually looking through this window from the outside, my gut instinct is that I want a model that's a more pessimistic version of the Erlang model. Mostly around assuming that not all processes are good guys (more opinionated and less generous supervisor), and back pressure for mailboxes, instead of having to roll your own cooperative overlay.

I would not be surprised if the latter in particular introduces scalability issues due to making asymmetric communications more symmetric, but if people are having to introduce their own wheels, it may be better if the system provided it instead.


Interesting. I remember when Java just got released my friend was so excited about Java in general and it's ability to load external class at runtime in particular. He had designed distributed actor system for it. He got his masters this way.

A bit later there was a company that has released generic java middleware called Voyager commercially. Among the other things it offered distributed actors as well. Unfortunately for whatever reason neither was a commercial success.



Probably a stupid question, but if you are building a sandbox on the server-side, why use WebAssembly rather than something more generic? There are so many sandboxing/container/VM environments available where you can run i86/arm code directly.


You can get a WebAssembly instance running in a few micro seconds. This makes it feasible to spawn a new sandbox for each request and scaling down to zero becomes almost trivial.

Another benefit is that you are not bound to a specific OS/arch. Lunatic lets you develop on macOs-arm64 and deploy on Windows-x64. Even container technologies widespread like Docker can't run on different CPU architectures. That proved to be a pain for people switching to the new M1 macs.


Just like any other bytecode format since early 1960's.


My guess is that the resource protection of WASM is quite well made.

There are experimental or hobbyist VMs with great resource protection but that didn't receive a lot of attention, so they have all kinds of sharp edges. And there are mainstream VMs that all completely suck at resource protection even when it's one of the main goals of the project.

On the middle, with a good enough to be useful amount of protection, and receiving attention enough to be usable, there is basically only WASM.

I imagine there is an amount of javascript-like "I want whatever I have on the browser, so I'll have to learn less stuff" happening too. But I don't think it's as relevant, because there is much less to learn when picking a VM.


Ok, how well are things like multithreading currently supported on WASM? How about shared memory (e.g. for efficient immutable structural sharing)? And does WASM support special instructions (memory barrier instructions come to mind) that allows one to implement (say) a garbage collected runtime on top of it?


The fact that it has a niche doesn't mean it's good for everything.


Because a new wave of startups need to replicate everything what Java and CLR have done the last 20 years, sell it as modern and here we go again at yet another industry cycle.




Consider applying for YC's first-ever Fall batch! Applications are open till Aug 27.

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

Search: