News for hackers. Stuff that matters.

snowboarder63

Re:Ractor – a Rust Actor Framework on Friday November 15, 2024 @07:48PM (#42153104)
Honest truth? I didn't Google it first lol. I just checked if there was a crate with the same name and carried on. I only found out about the Ruby ones after I posted on Reddit for the first time. By then it was too late since I had already reserved the crate name and it was being downloaded
Re:Ractor – a Rust Actor Framework on Friday November 15, 2024 @07:41PM (#42153043)
I actually have a side project doing this. I mapped an actor Id in ractor to a PID in erlang and used a rust create to implement the network protocol for erlang. I ran out of time on this side project kind of deal but it's like 90% there and can do a full cluster join and send and receive messages between remote actors on nodes.

The crate that helped this is erl_dist. I'd love to open source the code at some point, but it's not quite there yet

Re:Ractor – a Rust Actor Framework on Friday November 15, 2024 @07:39PM (#42153031)
Yeah I do lol. It was the big motivation since we were writing more and more rust and missed the concurrency model. Please feel free to ping me for any further questions

It's come a long way since it started and I'm thrilled I can talk about it publicly now and it's usage at Meta (some at least lol).

Thanks! I'm happy to see actors getting some solid use in the industry to provide better thread-management safety and remove a lot of concurrency headaches.

Question for you, I was poking around in the codebase and how do you handle your Signal priorities? Like if a link died, and there's 1000 messages in the queue already, or if it's a bounded mailbox, would the link died (or even stop) messages be delayed by that much?

Have you looked into prioritization of those messages such that it's not globally FIFO?

Also ractor is used in production at Meta. (Which I can finally say publicly lol)

Here's the RustConf presentation for anyone interested https://slawlor.github.io/ractor/assets/rustconf2024_present...

While the state is indeed a separate struct in ractor there's actually a good reason for this. It's because the state is constructed by the actor and it's guaranteed that the construction is the state is managed by the startup flow and panic safe.

Imagine opening a socket, if you have a mutable self the caller who is going to spawn that actor needs to open the socket themselves and risk the failure there. Instead of the actor who would eventually be responsible for said socket. This is outlined in our docs the motivation for this. Essentially the actor is responsible for creation of their state and any risks associated with that.

- for the async trait point we actually do support the native async traits without the boxing magic macro. It's a feature you can disable if you so wish but it impacts factories since you can't then box traits with native future returns https://github.com/slawlor/ractor/pull/202

(For transparency I'm the author of ractor)

I did not use ChatGPT to write this - Me lol
So it does indeed even guarantee that the server is acting in a trustworthy manner due to the public auditing scenario. We will shortly be making our audit logs publicly available to show that the verified crypto proof the client performs does indeed match the publicly available records. The academic works SEEMless (https://eprint.iacr.org/2018/607) and Parakeet (https://www.ndss-symposium.org/ndss-paper/parakeet-practical...) jointly outline how this all works from a technical perspective.

While we do maintain the directory, we are held to an honest standard by our audit logs. Should any auditor find invalid records, they can publicly hold us accountable.

WhatsApp announced that they’re adding key transparency to enhance end-to-end encryption verification within their suite of apps. There’s a blog post going into details on the engineering blog including announcing that the core logic for managing an auditable key directory is being open-sourced on Github: https://github.com/facebook/akd
Ah yeah we don't have hot-updates like Erlang does (we may never, it depends on how Rust would let us do it). That being said we aren't running on a runtime, so Rust code is native-compiled. You could take down and upgrade a node in a cluster as long as the network protocol doesn't change (or is at least backwards compat), but you wouldn't be able to like upgrade a single actor on a node.
Can you give an example of what you're referring to? I don't know of anything limiting memory / cpu / etc in Erlang at least of any individual gen_server. We have the Factory processes which can gracefully loadshed, but that doesn't stop you from having a memory leak.

At least Rust doesn't have a garbage collector, so when the actor is stopped + dropped, it'll cleanup not only it's state but also it's message queue's flushing them so that all memory is released at the time of drop.

Not sure what's public here, so let's just say "big", as in 100's+.
I mean lunatic runs within Wasm, which isn't the end of the world, but we compile down to native. Lunatic generally seems to want to be the end-all of concurrency but we fit more into an existing Tokio-based environment.

As far as deployment, do you mean like how we came to build this? Or like how you'd actually deploy it...? Because that's really up to the program being built imo

That's essentially a process-abort level panic, which then your whole environment would die. Unfortunately there's not much we can do about that. This will catch all unwindable panics however, so that's quite a lot but yeah there's an edge-case.

https://doc.rust-lang.org/std/panic/fn.catch_unwind.html

So to your two parts.

1. If you would want independent message handlers, normally we'd just add functions off the state which would be called strait from the message handler's match statement. I.e. something like self.handle_msg_1(message, state, ...). We looked at having multiple handlers but you kind of either get into generic's hell or having to do some kind of ugly proc macro. We figured this left it up to the implementor on how you want to handle it

2. Sequential processing would really be a factory. That would do parallel work of jobs using a basic actor implementation while handling concurrent work limits, job routing, etc. Otherwise you could always spawn inside your actor and like a wait on multiple handles if you wanted, but that gets messy and kind of diverges from the true intent. There is a factory implementation in the base ractor lib, feel free to take a look!

We've traditionally had non-pure Erlang services at WA for things like backing storage, interfacing with other infra, etc. This is just sort of the natural extension of that. There might be some services which we eventually rewrite but for now it's more "new" stuff that's being built with this. Some things will get published soon-ish, but I can't say more at the moment.

EDIT: The rewrites I mentioned are not WA specific, more Meta which is rewriting a lot of C++ and Python stuff to Rust constantly :p

If the actor fails, it could have failed mid-mutation so we can't trust the "state" object. This would be the same if we passed in an owned state (rather than a mutable reference) and the handler didn't reply with a new state object. If a failure happens, the state is dropped there too (in Rust). I know it's not exactly how Erlang handles it, but it's a tradeoff we've accepted in this case. We're open to suggestions however!
Ah so presently we're using the Tokio scheduler, which does cooperative scheduling not preemptive. We've abstracted pretty much all of our core concurrency primitives to a single module however so the plan is to support future schedulers (perhaps custom) in the future, but that's not the main focus at the moment. However if you have a blocking task, that will utilize a lot of sequential CPU time, the general guidance is to tokio::spawn_blocking(..) in your actor then await the result, so a new dedicated i/o thread will be used and your actor will yield the main scheduler for other workers.
We use both. This is running production workloads in WA today however Erlang is the "bulk" of our concurrency model, Rust is becoming more heavily used across all the products of Meta. You'll see more logic rewrites with Ractor coming out of Meta open-source in the coming months.
Yeah hot upgrades is something we'd love to get to, but probably won't be able to swing in a compiled language. We had worked out something ugly back in the day in a .net style environment (different library at a different job), but it still didn't compare to Erlang's hot upgrade flow.
I'll just say this, it's deployed in production scale at WhatsApp along side Erlang. Not as many services, but still serving the global user-base of WhatsApp. We have many services we've migrated to this where we had "hacked" together actor frameworks
That's really up to you, Rust has great pattern matching imho, rivaling Erlang's for sure, but with some nice syntax upgrades. Additionally in Ractor, RPC's are strong-typed to the reply type, so a `call` in Erlang might result in the wrong type coming back which then you have to handle in your reply pattern matching or crash. This isn't possible in Rust w/ Ractor, since you _know_ the strong type in advance and can't send an incorrect value.
Panic's (which can be captured) are captured and will be propagated to the supervisors. That's one of the biggest goals of building this, panics in tasks which have been spawned is a nightmare to deal with
Actually I was not familiar with Orleans until now. I think there's probably similar primitives you could build but from my quick 2s read they're solving a higher order problem. Additionally having written an actor lib in the past in .net, I can say they can't match rust for speed but they do have some nice syntax stuff with reflection we can't do.

So it's a tradeoff on stuff like syntactic sugar and speed imo

Actually no, I came up with the name independently and only later discovered their lib. I think it's safe to say we diverge a lot in practice though lol

Show HN: Ractor – a Rust-based actor framework with clusters and supervisors

https://github.com/slawlor/ractor

SOME HIGHLIGHTS. * We have a full supervision tree so actors can monitor other actors for exits and unhandled panic!s (at least the ones that can be caught) * The actor lifecycle is handled for you in a simple single-threaded, message handler primitive * You have a mutable state with each message handling call, so you have an easy way to create stateful actors and update that state as messages...

( Read More... | 77 comments | on HN )