riker-rs / riker

Easily build efficient, highly concurrent and resilient applications. An Actor Framework for Rust.
https://riker.rs
MIT License
1.01k stars 71 forks source link

No object-capabilities? #29

Closed WildCryptoFox closed 5 years ago

WildCryptoFox commented 5 years ago

The selectors directly break object capabilities. Although actors may be used without capabilities, I don't see why you wouldn't unless you're not aware of capabilities?

I don't see any references to asynchronous messaging (where an actor can do other work while it waits to send a message), nor anything that would prevent this due to the flexibility in futures-rs. If you're using unbounded channels, then the system may consume all resources due to the lack of back-pressure. If they are bounded, you may run into deadlocks.

If you support asynchronous messaging and other optimizations such as promise pipelines, you'll need to be careful to enforce E-order such that messages are delivered in a valid partial order.

I'm working on my own actor system in Rust, not yet published, featuring ocaps with bounded buffers and actors may continue to do other work while they wait to send messages.

I'd be surprised if you haven't already found this, but just in case you haven't, I strongly recommend this thesis. 'Robust Composition: Towards a Unified Approach to Access Control and Concurrency Control' - Mark Samuel Miller

leenozara commented 5 years ago

Hi @james-darkfox thanks for your interest in Riker. You have several points in your comment so let me deconstruct those:

Selectors do break the object capability model. This is an intentional feature. Generally speaking, with regards to Actors in general, it has been found that there is greater value allowing the freedom to address and interact with Actors by their path in the hierarchy without creating or being endowed with the actor reference over the security of enforcing object capability. This is why popular frameworks such as Akka have elected to do this.

Having said that, I do perceive some value in limiting interaction on an opt-in basis and the longer term plan is to allow an actor to be marked private. A private actor would be only addressable via its actor reference. Vernon touches on this in his popular book Reactive Messaging Patterns: https://www.oreilly.com/library/view/reactive-messaging-patterns/9780133846904/

With regards to asynchronous messaging, actors in Riker send messages asynchronously without blocking. It simply isn't an actor system without asynchronous messaging. Messaging is backed by an unbounded queue has you've discovered. This is by design. Features such as backpressure are better handled by Streams, or if within an Actor context then a backpressure pattern can be employed, something that Vernon also touches on in his book.

Riker actors are not attempting to reproduce features of Rust Futures or Streams. Users are encouraged to use actors in combination of futures, especially now with the improved futures in Rust 2018 and imminent release of Riker 0.1.9. Riker attempts to stick to the Actor = State + Behavior principle.

Riker provides a simple At-most-once message guarantee. This is also by design. The complexity of attempting to guarantee message order especially across networked nodes is out of scope of Riker's messaging design. We think the underlying ordering problem is better handled by using using some form of data distribution in the form of Vector Clocks or DHCTs.

Thank you for the link to the thesis. I personally am not aware of this and will certainly make some time to read it. It's much appreciated.

WildCryptoFox commented 5 years ago

Selectors do break the object capability model. This is an intentional feature. Generally speaking, with regards to Actors in general, it has been found that there is greater value allowing the freedom to address and interact with Actors by their path in the hierarchy without creating or being endowed with the actor reference over the security of enforcing object capability. This is why popular frameworks such as Akka have elected to do this.

If you can predict the hierarchy, why not just use a "root" handle and traverse from that node? This doesn't require breaking capabilities and enables this use case as opt-in, not opt-out. Doing so would enable you to sandbox actors by providing a different root.

With regards to asynchronous messaging, actors in Riker send messages asynchronously without blocking. It simply isn't an actor system without asynchronous messaging. Messaging is backed by an unbounded queue has you've discovered. This is by design. Features such as backpressure are better handled by Streams, or if within an Actor context then a backpressure pattern can be employed, something that Vernon also touches on in his book.

Not quite true, actors may be synchronous. I.e. seL4 threads use synchronous IPC. At least one side must block, while the other may use non-blocking operations. Promoting synchronous IPC to asynchronous is well defined. Either one side blocks, or a proxy does the blocking for both sides.

Futures' channels are this proxy and may be bounded. If bounded, one side still needs to decide if it'll block or retry later (and may do more work). Given this, E-order remains relevant. The unbounded buffer should be fine, but you can't defer to anything bounded without doing your part to enforce E-order.

Riker provides a simple At-most-once message guarantee. This is also by design. The complexity of attempting to guarantee message order especially across networked nodes is out of scope of Riker's messaging design. We think the underlying ordering problem is better handled by using using some form of data distribution in the form of Vector Clocks or DHCTs.

At-most-once delivery only handles reliability. So you're saying Riker messages are just unordered - relying entirely on synchronous signals to recover order? The unbounded buffer is totally ordered. Stronger than E-order, this is fine but now you can't do other optimizations such as MVCC/CoW-backed N-readers (read-only messages also don't need to be persisted in the journal as they don't influence state).

I don't think I've heard of DHCTs, do you have any links?

Thank you for the link to the thesis. I personally am not aware of this and will certainly make some time to read it. It's much appreciated.

Enjoy the read!

leenozara commented 5 years ago

Predicting an actor's path doesn't mean you can message it. It would simply mean that to message a private actor one would require the actor reference (the 'token' in Capability terminology), obtained directly by creating the actor or by being endowed through some other means. Attempting to message a private actor using its path would result in the same outcome as if the actor doesn't exist.

Like Akka and other popular actor frameworks we believe object capability is of lesser importance. If Riker were to provide this in the future we wouldn't abandon universality of actor paths by having multiple roots. Like a web domain's URI actor paths in Riker are unique and authorization as in the ability to message an actor will be handled by other means.

I think it's hard to qualify seL4 threads as actors. You could have two threads communicating over synchronous IPC such as using nanomsg, zeromq, etc but that doesn't qualify them as actors. In my opinion that seems more in line with the CSP model not the actor model.

Indeed, Riker makes no guarantees about message order. In a local system, where two actors are on the same node (local machine), then users will observe that there is 'same sender-receiver pair' message ordering. Since currently Riker does not have clustering or remote actors yet this is the case for all messages today. However this breaks down when messaging between actors located on separate systems on different nodes. This is why Hewitt rejected the requirement of message ordering: https://en.wikipedia.org/wiki/Actor_model#No_requirement_on_order_of_message_arrival

Sorry, I meant not say CRDTs, not DHCTs.

Cheers

WildCryptoFox commented 5 years ago

To restate my point: if you can do an ambient query for "/some/path", then you can eliminate the ambient authority by replacing it with an empty tree by default. Only if you want to use this power should you construct an all-powerful tree and delegate that to the actors you want to send messages to everything. If something is private, just don't add it to that tree. If you want to spawn an actor that assumes "/some/path" points to an actor of its interest, you can sandbox it to control where it points, such as to a proxy for journals or to filter which messages you want to allow it to send.

An actor only needs to be able to send messages to other actors and if it has state, mutate its state and maybe spawn more actors. If actors only ever do immediate behavior calls, they do not need asynchronous primitives at all. But if the actor system can form any cycles, it may lead to deadlocks. You only need unbounded buffers or retry on failure (possibly after performing more work and deciding to retry) to eliminate deadlocks. You only need to eliminate deadlocks if dependencies can form cycles.

I didn't mean to say seL4 threads are actors, I meant the synchronous IPC seL4 provides is sufficient for actors.

That argument against ordering misses the details of E-order. E-order doesn't forbid you from sending messages out of order, it prevents you from sending messages before any handles you currently hold. I.e. An actor may clone a capability and transfer that copy to one of their peers. Either party may invoke their capability without waiting for the other party's messages to be delivered.

Alternatively, if an actor Alice wishes to send some messages to Bob and then send Carol a handle to Bob. With E-order she can clone her capability after the point she already sent her messages to Bob and send that to Carol; the right to send messages to Bob is now a Future/Promise to do so, that is resolved after the appropriate dependencies have been delivered. Now Carol may start sending messages to Bob. Regardless on the latency properties of the graph, Alice' messages must be delivered before Carol's messages to Bob.

Without an encoding of this dependency, Alice must wait for Bob to acknowledge receiving her messages before she can delegate the right to send messages to Bob, to Carol; to keep the E-order semantic. Promise pipelines reduce round trips and thus latency, to enforce the same semantics - but must encode the E-ordering in the messages to be safe.

CRDTs, yes I know these. :)

Edit: To clarify for the message ordering. If you use synchronous channels, you get E-order. If you use asynchronous message passing, you need to be careful to keep E-order. Both IPC (a)sync options resist deadlocks and are consistent. Synchronous requires deciding to retry or abort and may do other work. Async just moves the blocking to a buffer. Futures are synchronous. (Un)bounded buffers are asynchronous. Bounded buffers, when filled, are synchronous. Synchronous is required for back-pressure.

Unbounded buffers on a single machine yields total order, not unordered. Total order is too strong to enforce in a distributed protocol without adding undesired roundtrips. Promise pipelines preserve E-order by using a DAG, exactly as futures-rs does with its demand-driven FSA-based generators.

* Futures are a synchronous form of message passing designed for asynchronous computations through composing them and deciding to block on vs. to just try, some actions.

leenozara commented 5 years ago

This is a very interesting conversation, let me say upfront I really appreciate it, just in case my responses fail to show that.

The actor hierarchy is used for more than select. Riker actors use a parental supervision strategy. Even if an actor would be private to the user, it still forms part of the hierarchy, where system messages are exchanged between actors. For example, you have an actor motion with private children limb1, limb2, limb3 and limb4. Since the children are private the only means to send messages to them is using the actor reference, which only the parent motion has. This prevents other actors using select to attempt to directly control individual limbs.

In addition if a child fails, its parent motion can determine what to do with the failed child. In this case it could choose to stop the failed child an update its own behavior to use a three-limbed movement model instead of four.

The actor hierarchy is the foundation of application resilience in Riker applications and the main purpose of a single 'all-powerful' tree. This might not be fully conveyed in Riker's documentation - there needs to be more work to improve that - but you can read this concept in detail in established actor frameworks, such as Akka: https://doc.akka.io/docs/akka/2.5/guide/actors-intro.html and here https://doc.akka.io/docs/akka/2.5/guide/tutorial_1.html.

Understood now with your example regarding sync messaging. Yes from that perspective futures are synchronous, i.e. any future in a chain of futures isn't buffering messages, at least as part of them being driven to completion.

You can send futures to Riker actors. In your example you could send a Future<Output=ActorRef> to an actor that once fulfilled can start sending its own messages. You can also combine the Ask and Pipe patterns (Ask is available in riker-patterns, but there's no Pipe, you'd have to roll your own for now). Again there's no eluding to this in the Riker documentation yet, unfortunately, but to use Akka's example you can look here: https://doc.akka.io/docs/akka/2.5.5/scala/actors.html#ask-send-and-receive-future.

Also FYI, Riker actors are also futures, they get executed with other futures on the configured dispatcher/executor, by default futures::executor::ThreadPool. They are however not designed to be composible.

WildCryptoFox commented 5 years ago

Akka's actors are one that I haven't really looked into. I've been focusing on E, Erlang, and Cap'n Proto's models. All of which are strongly tied to capabilities. Thanks for the links and for your quick responses. Good luck with the project! If you'd be interested, I can keep you updated when my actor framework is published. Many differences from Riker! :)

Feel free to close this thread if you're not going to confine the selection tree.

leenozara commented 5 years ago

Please do let me know when you publish your project - I'll certainly take a look. Any other actor related ideas to share or topics to discuss let me know. More than happy to engage.

Good luck!