1

How would I implement this tokio_postgres example with a custom tokio runtime builder and without the main macro?

This works fine, per the tokio_postgres docs:

examples/withmacro.rs

use tokio_postgres::{NoTls, Error};

async fn db_main()-> Result<(), Error> {
    
    // pasted from: https://docs.rs/tokio-postgres/0.6.0/tokio_postgres/index.html
    
    // Connect to the database.
    let conn_string = std::env::var("PG_CONNECT").unwrap();
    let (client, connection) = tokio_postgres::connect(&conn_string,NoTls).await?;
    
    // The connection object performs the actual communication
    tokio::spawn(async move {
        if let Err(e) = connection.await {
            eprintln!("connection error: {}", e);
        }
    });

    // Now we can execute a simple statement that just returns its parameter.
    let rows = client
        .query("SELECT $1::TEXT", &[&"hello world"])
        .await?;

    // And then check that we got back the same string we sent over.
    let value: &str = rows[0].get(0);
    println!("value: {:?}", &value);
    assert_eq!(value, "hello world");

    Ok(())
}

#[tokio::main]
async fn main() {
    dotenv::dotenv().ok();
    let _ = db_main().await;
}

However, I'd like to customize the tokio runtime builder as in the main below--and not use the tokio macro. This, however, panics saying "there is no reactor running" when it comes to the "tokio_postgres::connect" at runtime. How should I reconfigure the following code to use my own Tokio runtime instance within tokio_postgres (assuming that's even what I need)?

examples/nomacro.rs

use tokio_postgres::{NoTls, Error};

async fn db_main()-> Result<(), Error> {
    // Connect to the database.
    let conn_string = std::env::var("PG_CONNECT").unwrap();
    
    // This line panics with: 
    // "thread 'main' panicked at 'there is no reactor running, must be called from the context of Tokio runtime', /Users/me/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.3.2/src/io/driver/mod.rs:254:13"
    let (client, connection) = tokio_postgres::connect(&conn_string, NoTls).await?;
    
    // ...
    Ok(())
}
    
fn main() {
    dotenv::dotenv().ok();

    // Tokio 0.3
    let rt:Runtime = tokio::runtime::Builder::new_multi_thread()
        .worker_threads(6)
        .thread_name("my thread")
        .build()
        .unwrap();

    rt.block_on( async {
        db_main().await;
    });
}

Should I be passing a reference to the runtime to db_main() to give to an instance of tokio_postgres Config? I've experimented with disabling the tokio instance in tokio_postgres with "tokio-postgres = { version = "0.6.0", default-features = false}" in Cargo.toml.

Baseline Cargo.toml:

[dependencies]
dotenv = "0.15.0"
tokio = { version = "0.3", features = ["rt-multi-thread", "macros"] }
tokio-postgres = { version = "0.6.0"}
# tokio-postgres = { version = "0.6.0", default-features = false}
pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
cloudsurfin
  • 2,467
  • 2
  • 25
  • 29

1 Answers1

0

Very much learning (rust, tokio, postgres), I was cued by this issue to enable_io() which I tried on a whim for a working solution:

let rt = tokio::runtime::Builder::new_multi_thread()
    .worker_threads(6)
    .thread_name("my thread")
    .enable_io()
    .build()
    .unwrap();

I gladly defer to those wiser in Tokio. It may be a case of evolving documentation. The panic originates here in Tokio. And while intuitive that Postgres would require "io", the examples for Tokio Builder, tokio_postgres, and the code below do not allude to needing enable_io() on the Tokio builder for tokio_postgres to work. If I didn't question this was an entirely wrong approach I'd put in a doc pull request.

From tokio/src/io/driver/mod.rs

cfg_rt! {
impl Handle {
    /// Returns a handle to the current reactor
    ///
    /// # Panics
    ///
    /// This function panics if there is no current reactor set and `rt` feature
    /// flag is not enabled.
    pub(super) fn current() -> Self {
        crate::runtime::context::io_handle()
            .expect("there is no reactor running, must be called from the context of Tokio runtime")
        }
    }
}
pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
cloudsurfin
  • 2,467
  • 2
  • 25
  • 29
  • 1
    FWIW you can use `cargo expand` with the `tokio::main` macro to see what it expands to. And the answer is that it uses `enable_all`, which enables `io` and `time`. Might be safer to use that as well given `tokio_posgres` explitly says that it needs the `time` feature in its default configuration. – Masklinn Nov 04 '20 at 12:34