riker-rs / riker Goto Github PK
View Code? Open in Web Editor NEWEasily build efficient, highly concurrent and resilient applications. An Actor Framework for Rust.
Home Page: https://riker.rs
License: MIT License
Easily build efficient, highly concurrent and resilient applications. An Actor Framework for Rust.
Home Page: https://riker.rs
License: MIT License
Will riker build on stable rust in the near future?
What hurdles are there for building on stable?
ctx.select("/user/foo/*")
causes a panic.
Currently, there is no way to terminate the actor from recv callback in a way, that no messages will arrive (recv will not be called) after termination. Clearly ctx.stop(ctx.myself());
does not satisfy this criteria.
Here is the typical use case:
impl Actor for MyActor {
type Msg = MyEvent;
fn post_start(&mut self, ctx: &Context<Self::Msg>) {
// do request wait for MyEvent::Response
issue_request_to_someont(ctx.myself());
// or MyEvent::Timeout
ctx.schedule_once(ctx.myself(), Duration::from_sec(1), MyEvent::Timeout)
}
fn recv(&mut self, ctx: &Context<Self::Msg>, msg: Self::Msg, _sender: Sender) {
match msg {
MyEvent::Response(_) => {
// handle response
},
MyEvent::Timeout => {
// handle timeout
}
}
// missing :(
ctx.terminate();
}
}
In Erlang, I would simply stop receiving messages and leave the process function. Since here we are based on callback, I believe that such a function is really important.
Currently there is no clear way how to wait for the actor system to complete as the main thread would exit, since creation of the actor system dose not block.
After discussing this issue with @leenozara and @riemass and others we got these proposals:
1 - one way to add a method to the ActorSystem
like .wait()
that returns a rx
from a oneshot channel (acts like a future) and then using block_on(sys.wait())
to wait until this future resolves, and it will only resolve on system shutdown event.
2 - similar to the previous proposal but @leenozara introduced the when_terminated()
instead like in the akka's when_terminated and it would return the Terminated
(type alias for Receiver<()> for now), and I started to build on this idea by adding another feature that would also enable to get notified in another places on the application when the system is terminated, the idea also is inspired by akka ActorSystem
, adding a sys.register_on_termination(fn)
by register a callback to run after system is terminated, and the termination event/message has been issued and all actors in this actor system have been stopped. that also would be possible to add multiply callbacks by calling this method multiple times. Note that ActorSystem
will not terminate until all the registered callbacks are finished. (which would be a good place to add a tx
to send you a notification when the system is terminated).
3- added by @leenozara and @riemass too, We have channels that are PubSub
and we have ask
that is a tmp
actor bridging the actor space and main thread space as a future. One idea could be to combine those two, for example using .when_terminated()
creates an ask
that subscribes to some SystemTerminatedChannel
. When shutdown
happens all the subscribed temporaty ask actors fulfill their futures.
Any other Ideas/proposals ?
I'm currently using auto-crlf in git. With newline_style "Unix" I always have "changed" files in git because the newlines don't match. Setting it to "Native" would solve that for me, but everyone who uses auto-crlf false will than have the same problem.
I think it would be better to leave the newlines as is while formatting because git already handles that conversion.
In addition to scheduling an actor the timer should support scheduling a function (or future).
cc @ghtyrant
I feel like these warnings are point towards a structural or architectural issue:
Checking riker v0.4.0 (/home/meena/src/ap/riker)
warning: module has the same name as its containing module
--> src/actor/mod.rs:1:1
|
1 | pub mod actor;
| ^^^^^^^^^^^^^^
|
= note: `#[warn(clippy::module_inception)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
warning: module has the same name as its containing module
--> src/kernel/mod.rs:1:1
|
1 | pub(crate) mod kernel;
| ^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
warning: module has the same name as its containing module
--> src/system/mod.rs:2:1
|
2 | pub(crate) mod system;
| ^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
warning: module has the same name as its containing module
--> src/actor/mod.rs:1:1
|
1 | pub mod actor;
| ^^^^^^^^^^^^^^
|
= note: `#[warn(clippy::module_inception)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
warning: module has the same name as its containing module
--> src/kernel/mod.rs:1:1
|
1 | pub(crate) mod kernel;
| ^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
warning: module has the same name as its containing module
--> src/system/mod.rs:2:1
|
2 | pub(crate) mod system;
| ^^^^^^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
Maybe it makes sense to inform channel subscriber, that subscription has been added to the subscription list?
Such notification can be useful in the case when subscriber has to be sure that it will be notified on something, it immediately initiated. This will help to avoid race conditions similar to what currently happens in ActorSystem::shutdown. I believe that this is quite a common pattern.
I'm getting this if i haven't setup the config toml:
thread 'tests::can_instantiate' panicked at 'called `Result::unwrap()` on an `Err` value: configuration file "config/riker.toml" not found', libcore/result.rs:945:5
it seems to me that default fallback config could work better than an unwrap
I was looking into mailbox code and nocited that std::sync::Mutex
is used in run_mailbox
which is called from async
block at kernel.rs:73
.
std::sync::Mutex
should not be used in an asynchronous environment, because a mutex acquisition can block an entire reactor. Instead a futures::lock::Mutex
should be used there to make sure the code won't block an executor.
I am new to Rust as well as the Actor Pattern on a whole, so I am trying to understand How do i integrate it with an existing web framework.
Should I include the code below within an actor (will it cause any performance issues?)
Should I include the code below within another thread. If so, how do I refer to all the actors.
Basically I am trying to forward all HTTP messages to existing actors that are running.
I did have a look at actix, but it doesn't support named actors(which is big for me) and docs aren't great.
#[macro_use] extern crate nickel;
use nickel::{Nickel, HttpRouter};
fn main() {
let mut server = Nickel::new();
server.get("/bar", middleware!("This is the /bar handler"));
server.get("/user/:userid", middleware! { |request|
format!("This is user: {:?}", request.param("userid"))
});
server.get("/a/*/d", middleware!("matches /a/b/d but not /a/b/c/d"));
server.get("/a/**/d", middleware!("This matches /a/b/d and also /a/b/c/d"));
server.listen("127.0.0.1:6767");
}
Line 33 in 7c06e29
Shouldn't it be Box::new(User {})
?
And the same problem in README.md
Just wanted to make this an official issue, because it's fairly critical if you're dealing with persistance. #40 fixes this 😄
i saw this in the docs but wasn't sure why
it seems like it would be advantageous to have an actor system in a module with its own protocol
what are the consequences of having more than one system running?
The cool feature in Riker will be grate.
there is more infos:
virtual-actors in http://dotnet.github.io/orleans/index.html
The #![feature(assoc_unix_epoch)]
is no longer required as it's now stabilized. Also incidentally: riker 0.1.8 compiles for stable rust just fine with that line removed.
Hi.
Thank you for making Riker!
In the "Message Types" section of the tutorial the code for implementing Receiver and Receiver both have _msg: Add ...
The example ran correctly after changing _msg: to Sub and Print, respectively.
I didn't try running it as is...
Thanks.
P.S. I am going to try to use Seed (a WASM web-framework) with Riker, just to experiment and learn. Is there anything you think I should know about using Riker with WASM?
Noob to webdev, Rust, actors, distributed computing, etc. (pretty much a noob to all this tech with Rust being my first language for anything more than dabbling (dabbled with Haskell and Python each for a bit...)).
email is [email protected]
Hi, this is more of a question than an issue.
I am new to Actor model and trying to figure out how it works while reading some papers on it. So please bear with me.
Here I am trying to get response for the messages I sent.
extern crate riker;
extern crate riker_default;
#[macro_use]
extern crate log;
use riker::actors::*;
use riker_default::DefaultModel;
use std::time::Duration;
struct Counter {
count: i32,
}
impl Counter {
fn get_count(&mut self) -> i32 {
self.count
}
fn new() -> BoxActor<Opr> {
Box::new(Counter{ count: 0 })
}
}
#[derive(Debug, Clone)]
enum Opr {
Add(i32),
Sub(i32)
}
impl Into<ActorMsg<Opr>> for Opr {
fn into(self) -> ActorMsg<Opr> {
ActorMsg::User(self)
}
}
impl Actor for Counter {
type Msg = Opr;
fn receive(
&mut self,
_ctx: &Context<Self::Msg>,
msg: Self::Msg,
_sender: Option<ActorRef<Self::Msg>>,
) {
match msg {
Opr::Add(n) => {
self.count += n;
}
Opr::Sub(n) => {
self.count -= n;
}
}
}
}
fn main() {
let model: DefaultModel<Opr> = DefaultModel::new();
let sys = ActorSystem::new(&model).unwrap();
let props = Props::new(Box::new(Counter::new));
let my_actor = sys.actor_of(props, "my-actor").unwrap();
my_actor.tell(Opr::Add(10), None);
my_actor.tell(Opr::Sub(3), None);
std::thread::sleep(Duration::from_millis(500));
}
From looking at the Actor trait's receive method, currently that should not be possible, right?
pub trait Actor : Send {
type Msg: Message;
// ...
fn receive(&mut self, ctx: &Context<Self::Msg>, msg: Self::Msg, sender: Option<ActorRef<Self::Msg>>);
// ...
}
Hewitt writes,
Messages in the Actor Model are decoupled from the sender and are delivered by the system on a best efforts basis.
If an Actor is sent a request, then the continuation must be one of the following two mutually exclusive possibilities:
- to process the response resulting from the recipient receiving the request
- to throw a Messaging exception
Just sitting there forever after a request has been sent is a silent failure, which is unacceptable. So, in due course, the infrastructure must throw a Messaging exception as governed by the policies in place if a response (return value or exception) to the request has not been received.
Ideally, if the continuation of sending a request is to throw a Messaging exception, then the sender of a response to the request also receives a Messaging exception saying that the response could not be processed.
If desired, things can be arranged so that Messaging exceptions are distinguished from all other exceptions.
Runtime failures are always a possibility in Actor systems and are dealt with by runtime infrastructures. Message acknowledgement, reception, and response cannot be guaranteed although best efforts are made.
So, does current implementation fit into this description? How can I get a response for the message I sent?
Also, let's say I like to have two different messages rather than an enum, so that each has its own impl blocks:
struct Add(i32);
impl Add {
// ...
}
struct Sub(i32);
impl Sub {
// ...
}
So, to handle them, still I have to use pattern matching in receive function, right? Wouldn't it be better to have a separate handler for each message?
Hey! I've been monitoring this project for a little while, and I'm super excited to see how far it's come. Thanks for all your work so far.
I'm hoping to maybe even start using this at work (although Rust is still a hard sell, yet). I was wondering if you could give a little background on who the maintainers of this project are, what your plans for the project are, how development is sustained, and that kind of context. I'm asking a little ways ahead of when I might need to pitch at work because I'm gonna have to jump through some compliance hoops, and having good stories to tell about each of the libraries I'd recommend would be super helpful.
That said, I understand I'm asking for info which might be considered personal, this is completely optional, and I have no expectations here. I'm asking from a place of preparation and hope 😄
Thanks!
I'm probably asking for something whose difficulty far outweighs its worth, but I'm personally attempting to create an OS kernel built on an actor system, which naturally doesn't support std. I figure, instead of reimplementing everything manually, it would be nice to be able to reuse some of the abstractions from this crate. Namely some of the key traits for the most part. From a cursory glance at the code, the most that the critical parts depend on seems to be Arc
, which would be available with just alloc
.
Feel free to close as Won't fix
if this would be too much trouble for too little gain. I mostly just like seeing such a wide range of libraries that can be used in a freestanding environment.
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
The readme talks about remote actors, which looks super interesting! Are there more details on how that would work?
One usage example would be to build distributed apps with one side being a server others wasm compiled Riker code in web browsers.
New rustfmt.toml
file is ready.
Please review and comment on the settings. These are mostly sane defaults.
For feature merge requests it would be obligatory to follow the format, sparing time on manual formatting, having an consistent code and preventing situations where someone accidentally pushes someone else's code that was just reformatted.
Because most of the code is authored by @leenozara , it would be the best for if he could run cargo fmt
on the entire project and immediately push the formatted code.
Please note that for rustfmt versions before "1.3.3" fail to format kernel_ref.rs. The required version is available in today's nightly release, or can be downloaded from the rustfmt project repository.
If an actor is killed twice (either in a row or from consecutive messages), the kernel will panic and take the entire system with it. As killing an actor which is already dead is a meaningless action, maybe it would be better just to warn!
and let the second kill fall through.
Here is example code to reproduce:
use riker::actors::*;
use riker_default::DefaultModel;
pub struct TestActor {
}
impl TestActor {
pub fn actor() -> BoxActor<u32> {
Box::new(TestActor { })
}
pub fn props() -> BoxActorProd<u32> {
Props::new(Box::new(TestActor::actor))
}
}
impl Actor for TestActor {
type Msg = u32;
fn receive(&mut self,
ctx: &Context<u32>,
_: u32,
_sender: Option<ActorRef<u32>>) {
ctx.stop(&ctx.myself());
}
}
pub fn main() {
let asys = ActorSystem::new(&DefaultModel::<u32>::new()).unwrap();
let a = asys.actor_of(TestActor::props(), "test-actor").unwrap();
a.tell(0, None);
a.tell(0, None);
asys.shutdown();
}
Here is the output:
Starting actor system: System[riker]
2018-09-12 06:54:14+00:00 DEBUG [riker::system::system] Actor system [fcd7cdc2-3639-4a2f-9d6a-b58f703862b9] [riker] started
thread '<unnamed>' panicked at 'ACTOR DOCK NONE ON TERMINATE. THIS SHOULD NEVER HAPPEN', /home/micucci/.cargo/registry/src/github.com-1ecc6299db9ec823/riker-0.1.7/src/kernel/kernel.rs:229:17
error[E0554]: #![feature] may not be used on the stable release channel
--> src\lib.rs:2:1
|
2 | #![feature(async_await)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try rustc --explain E0554
.
error: Could not compile riker
.
Currently schedule_at_time
takesSystemTime
as parameter. This doesn't really work and instead should be replaced with DateTime
.
could be cool to have a channel to discuss riker
is it possible to create a new channel under the main rust slack?
I dont think it’s a good idea to drop info about exact versions of project’s dependencies out of repository
thanks
Dear all,
I would like us to begin to discuss how we can move forward with Riker development, both from a code perspective but maybe also a wider project concept.
I created Riker a couple of years ago, which itself was a reiteration of a previous closed source project from the early days of Rust. Riker has come a long way and I’m pretty proud of the result. I must say that the best updates to the project have come from others discussing and making PRs, rather than myself. Thank you to everyone that has given their time to improving Riker!
For Riker to now evolve, for it to see its potential, we must change the way the project is developed. It’s simply not enough to have one person setting the direction, without communication, and then relying on others to pick through code to see where changes might be useful. Collaboration needs to start at the design and roadmap phases. We need thoughtful discussion, a sense of collective ownership as well as specialist ownership of specific systems.
To be clear, I am committed to Riker. This isn’t me offloading the project to any group of people that want to take it. My skills as a Rust developer however aren’t nearly as good as many of you and my value is more on actor system design and concepts. I’m also in a position to fund server/hosting/build costs that save time, etc. If we continue to evolve I imagine other costs will emerge.
We’ve always tried to maintain a few core principles:
I had originally planned to create a roadmap, contribution guide, GitHub labels, etc and communicating this to everyone earlier last month. However, it became clear that even these must be done collaboratively.
So with a bit of the background laid out, what I would like to do is invite you all to share your thoughts and ideas here. The immediate main two questions I have are:
Let’s keep in mind that it might take some contributors a few days to see this issue. I’d like to give enough time to hear from new and old members of the community, as well as any one who would like to join but hasn’t yet.
Finally, this is certainly a discussion. Not limited to sharing ideas specifically with me, but to actually discussing between us all.
Thank you all and I hope everyone is starting safe. Looking forward!
For my understanding about actor model, i need 1 actor keep looping to do some task it should not hang the system, other actors should still working and do their job
Hello there, I'm trying to spawn futures inside my actor but I'm not sure how to get access to the inner ThreadPool
. This is as far as I got:
#![feature(async_await, await_macro, futures_api, rustc_private)]
extern crate riker;
#[macro_use]
extern crate log;
use futures::executor::block_on;
use riker::actors::*;
use riker_default::*;
use riker_patterns::ask::ask;
struct MyActor;
impl Actor for MyActor {
type Msg = String;
fn receive(
&mut self,
ctx: &Context<Self::Msg>,
msg: Self::Msg,
sender: Option<ActorRef<Self::Msg>>,
) {
let myself = ctx.myself();
let sender = sender.unwrap();
ctx.execute(async move {
debug!("Got message {:?}", msg);
sender.tell("This is your answer", Some(myself));
});
}
}
impl MyActor {
fn actor() -> BoxActor<String> {
Box::new(MyActor)
}
fn props() -> BoxActorProd<String> {
Props::new(Box::new(MyActor::actor))
}
}
// start the system and create an actor
fn main() {
let model: DefaultModel<String> = DefaultModel::new();
let sys = ActorSystem::new(&model).unwrap();
let props = MyActor::props();
let actor = sys.actor_of(props, "my-actor").unwrap();
let res = ask(&sys, &actor, "Hello world");
debug!("Got response {:?}", block_on(res));
}
I understand that Context::execute
returns a RemoteHandle
but I'm not sure where I should spawn it.
I understand how I would return messages to a non actor with the ask pattern, but is it possible to do that after persisting?
fn receive(&mut self,
ctx: &Context<Self::Msg>,
msg: Self::Msg,
sender: Option<ActorRef<Self::Msg>>) {
ctx.persist_event(msg);
}
fn apply_event(&mut self, _ctx: &Context<Self::Msg>, evt: Self::Msg) {
// how could I access `sender` here?
}
It would be nice to have a filter on the main logger, as sometimes the logging gets pretty verbose when many actors are present using many other libraries which all have logs flowing through. Since the Riker logger is an actor itself, this should be fairly easy to set up.
I was thinking just a simple array of terms which will be filtered out which will then do a simple check on each log. Since this array should hopefully be fairly small, and the logging strings not too large, I was thinking this would be simple but still not too much of a hit performance-wise.
Note: I have a basic implementation of a filter logger with this set up already written. I will post a PR so it can be reviewed.
Thanks!
Instant
is intended to be used for timers. SystemTime
can go backwards, e.g. if the system clock is adjusted. If the clock goes backwards, then everything relying on a timer would stop working until time catches up again (e.g. hours/days later). See the docs in the std lib.
error: aborting due to 6 previous errors
For more information about this error, try rustc --explain E0554
.
error: Could not compile runtime-fmt
.
.select
needs to be made available on Context
.
E.g. ctx.select
in transforming a project to async, we've hit a bit of a wall: Plume-org/Plume#777
that wall is the database, or the way we currently (have to) use it.
i was looking into using riker as the actor framework of choice, instead of creating an ad-hoc solution by ourselves
based on some unsuccessful experiments however, I'm not sure riker alone will do.
especially considering this comment:
#98 (comment)
if my Context
is a Connection
, that can't be read only!
but it also doesn't make sense for the Actor
holding a Connection
Context
, to recv
a Connection
Msg
the correct message would be either String
or Statement
i feel like the tools are there in riker to do this, buti don't have the understanding of how to put them together yet
(and if they aren't in riker alone, they should be in https://github.com/riker-rs/riker-cqrs
(but that library's documentation is even more incomprehensible to me))
I'm curious, would it make sense to have Actor::recv
an async fn? That way you could use futures inside of them with .await
and avoid having to ctx.run()
and block_on
.
The problem I would see with it would be that you wouldn't want to do blocking operations in a recv
implementation because it would stall the executor. Tokio handles those situations with a spawn_blocking(|| do_blocking_stuff())
pattern. That might be something to consider.
Anyway, I just wanted to open up the discussion and see what thoughts were on it. I'm really liking the look of Riker so far.
It's possible to override the location using an env variable:
RIKER_CONF
to override the full path to riker.toml
APP_CONF
to override the full path to app.toml
Example:
export RIKER_CONF=/var/conf/riker/riker.toml
.
This feature is already there but not documented. I'll add an issue to add this to the documentation.
Code in https://github.com/riker-rs/riker/blob/master/src/kernel/mailbox.rs#L307-L311 is commented out, so Actor::post_start is never called.
When changes to kernel
and system
modules are made, as well as changes to dependencies, we should run a series of standard benchmarks. These would include both rust/cargo benchmarks and common actor system benchmarks (such as Ring Token and Skynet).
These tests would provide a benchmark between riker versions to ensure changes made don’t impact performance.
Additionally, benchmarks could be made between different actor systems both rust based and Scala, Erlang, C++, etc. This is less of a priority however at this point.
See:
https://arxiv.org/pdf/1505.07368.pdf
https://github.com/actor-framework/benchmarks
Currently ActorId
, which is type ActorId = u32
, is simply generated using a random u32
.
There is an unacceptable probability of the same u32 being generated in the same application. An alternative such as Uuid needs to be considered.
The current version still has alpha dependencies in the manifest file.
Would be great to update them to the released ones.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.