Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PoolTimedOut error with OnceCell due to OnceCell dropped with the test which initialized it #2881

Closed
kingwingfly opened this issue Nov 17, 2023 · 5 comments
Labels

Comments

@kingwingfly
Copy link

kingwingfly commented Nov 17, 2023

Bug Description

I put PgPool in a ModelManager, so that I can manage it conveniently. Then I init it using tokio::sync::OnceCell, so that all the test func use the same ModelManager.

And I have 2 tests, one acquire a PgPool connection, and another print something through a method on ModelManager.

Run test again and again, and about 1/5 tests will fail with error PoolTimedOut.

Minimal Reproduction

Find the the cause and fix solution here:
https://github.com/kingwingfly/share_runtime_example

For convenient:

#![allow(dead_code)]

use sqlx::postgres::{PgPool, PgPoolOptions};
use tokio::sync::OnceCell;

const PG_URL: &str = "postgres://postgres:postgres@localhost:5432/postgres";
static INIT: OnceCell<ModelManager> = OnceCell::const_new();

/// Init ModelManager for test, so that all the test func use the same ModelManager
async fn init_test() -> ModelManager {
    let mm = INIT
        .get_or_init(|| async {
            println!("INIT ModelManager");
            ModelManager::new().await
        })
        .await;
    mm.clone()
}

#[derive(Clone)]
struct ModelManager {
    // postgres connection pool
    pg: PgPool,
}

impl ModelManager {
    async fn new() -> Self {
        let pg = PgPoolOptions::new()
            .acquire_timeout(std::time::Duration::from_secs(2))
            .max_connections(4)
            .connect(PG_URL)
            .await
            .unwrap();
        Self { pg }
    }

    async fn pg(&self) {
        self.pg.acquire().await.unwrap();
    }
    async fn foo(&self) {
        println!("foo");
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test1() {
        let mm = init_test().await;
        mm.pg().await;
    }

    #[tokio::test]
    async fn test2() {
        let _mm = init_test().await;
    }
}

Info

  • SQLx version: 0.7.2; 0.6.3
  • SQLx features enabled: ["runtime-tokio-rustls", "postgres"]
  • Database server and version: Postgres 16.1 (docker); MySql:latest (docker)
  • Operating system: OSX
  • rustc --version: rustc 1.74.0 (79e9716c9 2023-11-13) and rustc 1.76.0-nightly (a57770440 2023-11-16)
@kingwingfly
Copy link
Author

Enable this will lower the possibility the bug happens, but cannot fix it.

PgPoolOptions::new()
    .test_before_acquire(false)
    .connect(&url);

@kingwingfly
Copy link
Author

I also added this

PgPoolOptions::new()
    .before_acquire(|_, _| {
        Box::pin(async {
            println!("1");
            Ok(true)
        })
    })

If the bug happen, nothing printed, if not, "1" printed.

Does it means acquire is even not called, then I got a TimedOut error?

@kingwingfly
Copy link
Author

kingwingfly commented Nov 17, 2023

I'm sorry, but it's due to I'm using tokio wrong way.
The reason is tokio-rs/tokio#6066 (comment)

Some issue concerned:
tokio-rs/tokio#6066
tokio-rs/tokio#2374

@kingwingfly kingwingfly changed the title PoolTimedOut error happens randomly in tests PoolTimedOut error with OnceCell due to OnceCell dropped with the test which initialized it Nov 17, 2023
chris13524 added a commit to reown-com/notify-server that referenced this issue Jan 22, 2024
chris13524 added a commit to reown-com/notify-server that referenced this issue Jan 23, 2024
chris13524 added a commit to reown-com/notify-server that referenced this issue Jan 23, 2024
* fix: use blocking relay subscribe everywhere

* chore: add Grafana metrics

* chore: increase timeouts

* chore: log expected tag and topic

* chore: switch to prod relay

* fix: replication lag

* chore: reduce replication lag sleep for now

* chore: increase message delivery timeout

* fix: update tokio to fix PoolTimedOut errors: launchbadge/sqlx#2881 (comment)

* chore: remove test parallel

* fix: use relay HTTP client for tests (#316)

* fix: use relay HTTP client for tests

* fix: run CI when tests are changed
@LennDG
Copy link

LennDG commented Feb 2, 2024

For anyone else coming here: the simplest way to solve this is by setting the max_connections to 1 during test, i.e.:

async fn new() -> Self {
        let max_connections = if cfg!(test) { 1 } else {4 };

        let pg = PgPoolOptions::new()
            .acquire_timeout(std::time::Duration::from_secs(2))
            .max_connections(max_connections )
            .connect(PG_URL)
            .await
            .unwrap();
        Self { pg }
    }

I am not entirely sure what causes this to fix it but I found it solved by this commit in a tutorial.

@kingwingfly
Copy link
Author

I have put the reason and fixing method here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants