Closed hardliner66 closed 1 year ago
So I'll have to look more into how actix
handles this under the hood, but do they support multiple Handler
definitions for different message types?
Also does a Handler
have to have a reply? Generally that's not true of an actor pattern, since there are both 1-way sends which should be non-blocking and 2-way sends with replies, which seems to be the only thing actix
has implemented?
I guess you could just not await the future, but that seems wrong than explicitly sending a 1-way message.... And if you await the reply always, you can (1) get deadlocks without realizing pretty easily (A triggers B, and waits on B's reply which requires pinging A. If A waits on the initial send to B, deadlock) and (2) If the actor has a long message queue, that await might take a long time to return when you don't need to process a result.
Yeah, they do. You just add a handler per message type.
I have a few ideas for "no reply". The simplest would be to make the response a ()
. But I'm not sure if that can be optimized away properly. Another would be to return an Option<T>
, but this would be unnecessary overhead when you always or never send a response.
Maybe two handlers, one with response, one without?
I think it might even be better to make handlers return nothing. If you need a response, you could then send a channel or something similar, which the handler uses to send something back. Then you could probably build convenience wrappers that work with return types, but use the primitives that already exist.
Maybe there is some inspiration to be had from CAF.
Yeah, they do. You just add a handler per message type.
Hmm this might be tricky for the cluster scenario. You'd need to know each message type and still be able to multiplex around them in order to send the right message over the link. Even from the actix
team they seem to hit a wall on this and give up (before giving up in-general on actix
actors) https://github.com/actix/actix-remote
It looks like CAF has network communication support, but I'll need to read more to see what the implications might be doing this.
Ah so I played around a tiny bit and it's more difficult than I originally thought for more basic reasons.
You have the basic Actor
trait which has a message type today (in ractor
). Moving to Handler
implementations means that you have an actor that may have 0..inf
message types to deal with. Unfortunately in the case of 0, there's not going to be any handle(..)
function to call, and the actor supports 0 message types. It looks like that's OK in actix
today... which is very strange. This unfortunately be a massive rewrite in our realization to make the message loop flexible enough to somehow support an actor with 0 handler implementations.
Either that, or the base actor trait would need to know all the message types it supports, which imo defeats the purpose of having the Handler
trait in this case. It looks like the way they achieved it was by having the message itself know the type and which handler to call which is probably why every send
operation is a future I'm guessing. https://github.com/actix/actix/blob/5d447fcd0a1ded1be1e189d57c6f1950d9d2ef6b/actix/src/contextitems.rs#L114
I'll keep this idea in mind, but I'm not sure (right now) if it'll fit into how we structured things. Thanks for the suggestion though!
Depending on how strongly typed everything is, it might be possible to send a message to an actor that doesn't support it. In this case, the actor would probably answer with something along the lines of MessageTypeNotSupported
. Having 0 handlers is the same. You try to send a message to an actor that doesn't support it, so it should also respond with that error.
I'm not too into the details of ractor, so I can't speak on whats possible now and what should/will be possible in the future, so this might never come up anyway, but being able to send untyped messages to actors might still be something to consider for building generic fan-out actors or similar.
I'm currently a bit sick, but I might have a look at the internals of ractor to see if it could be incorporated somehow.
CAF has network support, because every message must be serializable. So if you cross that border, it gets serialized and deserialized on the other side. But because it's C++, you can probably do some crazy tricks to see which messages have appropriate handlers.
CAF has network support, because every message must be serializable. So if you cross that border, it gets serialized and deserialized on the other side. But because it's C++, you can probably do some crazy tricks to see which messages have appropriate handlers.
Yeah we do a similar thing, but you can opt-in to serializability in ractor
when using it in a distributed cluster. Not every actor needs to implement the serializable traits if you're not going to send messages over a link. Anyways I look forward to any suggestions or contributions you have :)
I think having serializability as requirement would still be a big benefit, as you get location transparency for free. If every message is serializable, you don't need to think about which actor is on the same platform and which isn't. And you can move actors from local to remote without changing anything.
The way it's designed here, you don't need to think about which actor is where. If a actor is remote or local is completely transparent to the actor. The point of making it opt-in, is serializability isn't a trivial definition in some Rust structs (like discriminated union/enums). Additionally it allows you to be specific about some things, like serializing a usize
, that shouldn't be done at all due to the risk of varying architectures across a network of hosts.
However, once defined, if the actor is local or remote doesn't matter to all of the APIs and it's completely seemless. Actors which don't support remote-calling would just not be represented across the remote link in the remote node()
. If you make all actors have serializable message structures, they'll all appear on the remote host as available.
One of the things I like about actix, is the possibility to define handlers for multiple messages per actor, which is something that erlang, caf and akka (iirc) allow as well. I know that this can be emulated with enums, but having it work without would be a good quality of life improvement.
Actix Example: