2

I would like to initialize SeaOrm database connection in sync for easier usage inside application and testing, but not sure how to do it without error. I'm fine with any type of blocking as this happens only once. For testing #[tokio::test] is used.

In current example I get following error:

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.

#[tokio::main]
async fn main() {
    let database_service = DatabaseService::init();
}

pub struct DatabaseService {
    pub connection: DatabaseConnection,
}
impl DatabaseService {
    pub fn init() -> Self {
        let connection_options = ConnectOptions::new(&API_ENV.db_url);

        // tokio::runtime::Runtime::new() also gives similar error
        let connection = Handle::try_current()
            .expect("Can't get tokio runtime")
            .block_on(async { sea_orm::SqlxPostgresConnector::connect(connection_options).await })
            .expect("Can't connect to database");

        Self { connection }
    }
}

This is working with additional deps, but hangs in tests #[tokio::test] probably due to this:


// futures = "0.3.31"
// futures-executor = "0.3.31"


     let connection = futures::executor::block_on(sea_orm::SqlxPostgresConnector::connect(
            connection_options,
        ))
        .expect("Can't connect to database");
2
  • 3
    I'm curious why you're going through this hassle instead of just making the method async... the only calling contexts you mention or show are async (#[tokio::main] and #[tokio::test]).
    – kmdreko
    Commented Apr 13 at 16:30
  • @kmdreko you are right, I just thought it can be done easily and I can remove async/await in many tests
    – ZiiMakc
    Commented Apr 13 at 18:37

1 Answer 1

4

The only coherent way to block while executing code within a tokio executor thread, but not able to use await, is to call tokio::task::block_in_place(). This function will inform the executor that the thread is going to block, so that other work can be moved off the thread.

However, I strongly recommend that you not use this just for “easier usage inside application”. Even though block_in_place() is better than running a blocking operation without notifying Tokio about it, it can still have surprising behavior and you should not make your application more brittle just to feel more convenient to write. In software engineering, that’s a bad tradeoff that has a good chance of biting you later.

You should use spawn_blocking().await for blocking operations, and use await for async operations. Use block_in_place() only when it is impossible to have an await because you are writing code inside a non-async function, yet you are running code on an async executor thread anyway. That should be very rare and special circumstances.

5
  • Should blocking operations, specifically working with SQLite (rusqlite) use block_in_place() or should they be offloaded to a worker pool for instance using spawn_blocking() ? Commented Apr 13 at 16:49
  • @SvetlinZarev block_in_place is essentially never the best option, only a pragmatic one. I just realized that my answer might have been interpreted as recommending it, so I edited to clarify ("block_in_place() is better than running a blocking operation without notifying Tokio about it", not better than other tools such as spawn_blocking()).
    – Kevin Reid
    Commented Apr 14 at 2:07
  • What would be the reason to ever call block_in_place(x) instead of spawn_blocking(x).await.unwrap()? The only reason I see is to avoid the Send + 'static bound on the closure. (Another could be to avoid inter-thread synchronization cost, but that would be miniscule compared to the work being done, so that can't be the reason.) Commented Apr 14 at 11:58
  • 1
    @user4815162342 If you can write spawn_blocking(x).await, you should. I regret that my answer was, apparently, so unclear, and I've further edited to clarify.
    – Kevin Reid
    Commented Apr 14 at 14:56
  • Thanks for the edit, it's a great explanation of when block_in_place() is needed beyond what I saw previously. I also missed that the OP's code is in a non-async function, so (in their current design) they're stuck with block_in_place(), and in turn that's why your answer provides it as a solution. Commented Apr 14 at 15:05

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.