cloudflare / pingora

A library for building fast, reliable and evolvable network services.
Apache License 2.0
20.28k stars 1.1k forks source link

Cannot start a runtime from within a runtime. #169

Closed iwanbk closed 2 months ago

iwanbk commented 3 months ago

Describe the bug

i got this error when running the pingora app

thread 'main' panicked at /Users/ibk/.cargo/registry/src/index.crates.io-6f17d22bba15001f/pingora-core-0.1.0/src/server/mod.rs:302:57:
Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Pingora info

Please include the following information about your environment:

Pingora version: 0.1.0 Rust version: 1.77 Operating system version: Mac OS Sonoma 14.4

Steps to reproduce

In my main, i need to initialize a (non pingora) client which need await. So, the main fn need to become async and become like this

#[tokio::main]
async fn main() {

Looks like it is the one that create the problem.

Expected results

can run normally

Observed results

got above error

Additional context

My program need to talk to some backend service, hence the need to initialize the client.

andrewhavck commented 3 months ago

What are you trying to accomplish? We have a BackgroundService you can use to run background services and expose them to pingora.

eaufavor commented 3 months ago

tokio::main is just syntax sugar for

fn main() {
    let mut rt = tokio::runtime::Runtime::new().unwrap();
    rt.block_on(async {
        // your code
    })
}

So you can do that instead if you need to run async code before starting the pingora server.

iwanbk commented 3 months ago

What are you trying to accomplish?

@andrewhavck i need to initialize a redis client during startup, so it can be used on each request

So you can do that instead if you need to run async code before starting the pingora server.

Thanks @eaufavor , it answers my questions 👍

andrewhavck commented 3 months ago

i need to initialize a redis client during startup, so it can be used on each request

FYI as mentioned here if you start pingora with the daemonize flag it will fork() on run_forever() and Tokio runtimes created before then will likely not work.

iwanbk commented 3 months ago

FYI as mentioned here if you start pingora with the daemonize flag it will fork() on run_forever() and Tokio runtimes created before then will https://github.com/tokio-rs/tokio/issues/4301.

I see, honestly i haven't tried the solution yet when i closed this issue, because i move to something else right now. So, the solution is using Background Service @andrewhavck ?

vicanso commented 3 months ago

You can try to use like this:

#[tokio::main]
async fn run1() {
    // build our application with a route
    let app = Router::new()
        // `GET /` goes to `root`
        .route("/", get(root));
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

fn run() -> Result<(), Box<dyn Error>> {
    // init my server of pingora

    std::thread::spawn(|| {
        run1();
    });
    my_server.run_forever();
    Ok(())
}

fn main() {
    if let Err(err) = run() {
        error!("{}", err.to_string());
    }
}

I also find a way to run all service of pingora and I made a pull request, https://github.com/cloudflare/pingora/pull/175

andrewhavck commented 2 months ago

So, the solution is using Background Service

I don't think so unless you need to query Redis out of band of making a request, on a fixed interval or something like that.

Is there a reason the Redis client needs to be initialized in a separate Runtime before Pingora starts? Can you initialize the client lazily and then invoke it later from the request filter?

github-actions[bot] commented 2 months ago

This question has been stale for a week. It will be closed in an additional day if not updated.

github-actions[bot] commented 2 months ago

This issue has been closed because it has been stalled with no activity.

lukasmetzner commented 1 week ago

@iwanbk I dont know if it is still valid for you, but I could get it to work using Tokios implementation of OnceCell and its get_or_init method in the upstream_peer method. I am using the redis-rs redis::aio::ConnectionManager.

static REDIS_CLIENT: OnceCell<ConnectionManager> = OnceCell::const_new();