-
Notifications
You must be signed in to change notification settings - Fork 15
Description
I am using pgtemp across many unit tests and I'm a big fan. I am also using cargo nextest, spinning up many of these in parallel. I occasionally run into two issues which I believe may be related to each other and to issue #9
- Database still starting error: This is the following diesel error that I occasionally get from tests, where pgtemp believes it's initialised slightly sooner than it really has.
PoolError(Backend(ConnectionError(CouldntSetupConfiguration(DatabaseError(Unknown, "the database system is starting up")))))
- Database hangs on shutdown: I also occasionally experience 60s hangs at the end of tests while dropping tempdb, potentially the same as issue Shutdown (without persist) hangs in Github actions #9.
These happen locally and on Github CI, and infrequently enough that it's hard to figure out why. I am using postgres 14 and initialising via let _tempdb = pgtemp::PgTempDBBuilder::new().start_async().await, relying on the _tempdb variable to be dropped at the end of the test to shut down the temporary postgres instance.
My working hypothesis is that either pgtemp or postgres has a race condition when starting up multiple databases at the same time, perhaps picking up the wrong process ID or file somewhere? However, I can't artificially reproduce it with the test attached below. I'll add more information if some extra traces I've collected help.
The hang often happens to the same test (on 1 in every 10 test runs), and occasionally happens to other tests that start around the same time (on 1 in every 100 test runs). It's definitely the dropping of pgtemp that's hanging, which makes me think the order/timing that tests are started is having an impact.
This attempt to recreate it doesn't work though...
#[tokio::test]
async fn test_spinning_up_many_pgtemp_instances() {
// function to create and destroy many tempdbs sequentially, to simulate a single test thread
// running many tests sequentially
async fn sequential_create_destroy() {
for _ in 0..50 {
let start = std::time::Instant::now();
// create new tempdb
let temp = pgtemp::PgTempDBBuilder::new().start_async().await;
let mut pg_conn = sqlx::postgres::PgConnection::connect(
temp.connection_uri().as_str()
).await.unwrap();
// add some data
pg_conn.execute("CREATE SCHEMA test").await.unwrap();
pg_conn.execute("CREATE DATABASE test").await.unwrap();
pg_conn.execute("CREATE TABLE test.test_table (id INT)").await.unwrap();
pg_conn.execute("INSERT INTO test.test_table VALUES (1)").await.unwrap();
pg_conn.execute("INSERT INTO test.test_table VALUES (2)").await.unwrap();
// drop DB with an active connection
drop(temp);
let duration = start.elapsed();
if duration.as_secs() > 10 {
panic!("Creating and destroying a PgTempDB took too long: {:?}", duration);
}
}
}
// run many instances in parallel to simulate test runner threads
let mut instances = vec![];
for _ in 0..20 {
instances.push(sequential_create_destroy());
}
futures::future::join_all(instances).await;
}