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

Using Riker with Tokio #150

Open mankinskin opened 3 years ago

mankinskin commented 3 years ago

Hi, I am having some trouble spawning a future on riker's executor. I want to spawn a stream polling future that should forward messages to an actor. Basically this is the only interesting part:

fn pre_start(&mut self, ctx: &Context<Self::Msg>) {
    let myself = ctx.myself();
    self.handle = Some(ctx.run(async move {
        while let Some(res) = crate::telegram::telegram().api.stream().next().await {
            match res {
                Ok(update) => myself.tell(update, None),
                Err(e) => error!("{}", e),
            }
        }
    }).expect("Failed to spawn telegram stream!"));
}

then I create the actor in my main function. annotated with tokio::main:

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut telegram_actor = crate::actor_sys_mut().await.actor_of::<TelegramActor>("telegram-actor").unwrap();
}

Where actor_sys() lazily creates the ActorSystem:

lazy_static! {
    static ref ACTOR_SYS: Arc<RwLock<ActorSystem>> = Arc::new(RwLock::new(ActorSystem::new().unwrap()));
}
pub async fn actor_sys() -> RwLockReadGuard<'static, ActorSystem> {
    ACTOR_SYS.read().await
}
pub async fn actor_sys_mut() -> RwLockWriteGuard<'static, ActorSystem> {
    ACTOR_SYS.write().await
}

However when I run this with the ctx.run call, I get this error:

thread 'pool-thread-#3' panicked at 'there is no timer running, must be called from the context of Tokio runtime', /home/linusb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.24/src/time/driver/handle.rs:25:14

Am I doing something wrong? How can I solve this? Is there a better approach?

mankinskin commented 3 years ago

So the problem seems to be that I have a tokio executor running, but riker uses the executor from futures. I'm thinking about configuring riker to use the tokio executor now.

mankinskin commented 3 years ago

So this is my current state.

The ActorSystem starts the executor, which is currently hardcoded to futures::executor::ThreadPool, which is a handle to a ThreadPool, i.e. it can be cloned.

I have tried to make this field generic using an ExecutorHandle trait, but I realized that ActorSystem is passed all over the crate as a handle to the ActorSystem, so that meant adding generic parameters everywhere, which requires a lot of maintenance.

Then I tried to use cargo features to select the executor used using compiler flags. This got me pretty far, but there are some issues making the tests pass. I also had to edit riker-testkit. which seems to implement some mock actors for testing. I got it to compile, but for some reason the tests won't run through, they are just blocking and not being executed. Now I am looking into it on my forks tokio_executor branches: https://github.com/mankinskin/riker/tree/tokio_executor https://github.com/mankinskin/riker-testkit/tree/tokio_executor

mankinskin commented 3 years ago

I have come to believe that it would be better to migrate the whole crate to use async/await and a tokio executor, which also intersects with the discussion in #87 . I would probably remove the feature in my fork and focus on migrating the entire crate to tokio using as much async/await as possible. But I will have to get more familiar with the individual components of this crate first. Any comments or suggestions would be much appreciated.

mankinskin commented 3 years ago

The problem was not with riker or tokio, but rather telegram-bot.. It might be that there is an incompatibility between tokio 0.2 and 0.3

mankinskin commented 3 years ago

Actually, the problem was still there, even when using tokio 0.2 (in my crate). I thought the issue was that I was using telegram-bot (tokio 0.2) and with tokio 0.3, but that was not it. Even when using 0.2 everywhere, the futures executed by riker conflict with the tokio runtime.

The tokio_executor branch solves this though, by replacing the ThreadPool executor with a tokio::executor::Handle.

The code is a little bit ugly because of all the feature guards, and probably not ready to be merged in this repository. It works for my usecase right now, as I only really want to use tokio, but I would be happy to discuss how to turn this into a PR. Probably we can make the feature guards less extensive by encapsulating some parts of the code more.

BratSinot commented 2 years ago

@mankinskin Greetings again! I don't know it problem of Tokio or Rust, but using riker from your branch gives me about 74-76% higher RAM usage than stock lib.

mankinskin commented 2 years ago

@BratSinot this might well be due to tokio: https://www.reddit.com/r/rust/comments/i3fync/why_async_version_of_simple_program_use_to_50x/

I have not benchmarked this branch, but I don't think anything except tokio could allocate this much RAM. In the reddit answer they say it is not really used memory, but just virtual memory, i.e. reserved memory ready to use but not actually used.