diff --git a/Cargo.lock b/Cargo.lock index 975a9c3c..e49a1806 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,9 +42,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" +checksum = "602d785912f476e480434627e8732e6766b760c045bbf897d9dfaa9f4fbd399c" dependencies = [ "gimli", ] @@ -127,9 +127,9 @@ dependencies = [ [[package]] name = "async-std" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93c583a035d21e6d6f09adf48abfc55277bf48886406df370e5db6babe3ab98" +checksum = "00d68a33ebc8b57800847d00787307f84a562224a14db069b0acefe4c2abbf5d" dependencies = [ "async-attributes", "async-task", @@ -1576,9 +1576,9 @@ dependencies = [ [[package]] name = "paste" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" +checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" dependencies = [ "paste-impl", "proc-macro-hack", @@ -1586,14 +1586,11 @@ dependencies = [ [[package]] name = "paste-impl" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" +checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" dependencies = [ "proc-macro-hack", - "proc-macro2", - "quote", - "syn", ] [[package]] @@ -2042,18 +2039,18 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" -version = "1.0.112" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736aac72d1eafe8e5962d1d1c3d99b0df526015ba40915cb3c49d042e92ec243" +checksum = "6135c78461981c79497158ef777264c51d9d0f4f3fc3a4d22b915900e42dac6a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.112" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf0343ce212ac0d3d6afd9391ac8e9c9efe06b533c8d33f660f6390cc4093f57" +checksum = "93c5eaa17d0954cb481cdcfffe9d84fcfa7a1a9f2349271e678677be4c26ae31" dependencies = [ "proc-macro2", "quote", @@ -2176,9 +2173,9 @@ checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" [[package]] name = "smol" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f50d21240d7045d848746d6244762b8cb96449b586b4200519719784859ef50" +checksum = "4c3546640e104d1ee544df9c08ac4896d44ef1de89a6d7cc4a3c22f677d5ef38" dependencies = [ "async-task", "blocking", @@ -2553,9 +2550,9 @@ checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" [[package]] name = "syn" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" +checksum = "a994520748611c17d163e81b6c4a4b13d11b7f63884362ab2efac3aa9cf16d00" dependencies = [ "proc-macro2", "quote", diff --git a/sqlx-test/src/lib.rs b/sqlx-test/src/lib.rs index 55e4b85c..63cfc611 100644 --- a/sqlx-test/src/lib.rs +++ b/sqlx-test/src/lib.rs @@ -1,4 +1,4 @@ -use sqlx::{database::Database, Connect}; +use sqlx::{database::Database, Connect, Pool}; use std::env; fn setup_if_needed() { @@ -17,6 +17,25 @@ where Ok(DB::Connection::connect(&env::var("DATABASE_URL")?).await?) } + +// Make a new pool +// Ensure [dotenv] and [env_logger] have been setup +pub async fn pool() -> anyhow::Result> + where + DB: Database, +{ + setup_if_needed(); + + let pool = Pool::::builder() + .min_size(0) + .max_size(5) + .test_on_acquire(true) + .build(&env::var("DATABASE_URL")?) + .await?; + + Ok(pool) +} + // Test type encoding and decoding #[macro_export] macro_rules! test_type { diff --git a/tests/postgres/postgres.rs b/tests/postgres/postgres.rs index 0445028b..fc953807 100644 --- a/tests/postgres/postgres.rs +++ b/tests/postgres/postgres.rs @@ -1,8 +1,9 @@ use futures::TryStreamExt; use sqlx::postgres::PgRow; -use sqlx::postgres::{PgDatabaseError, PgErrorPosition, PgPool, PgSeverity}; -use sqlx::{postgres::Postgres, Connection, Executor, Row}; +use sqlx::postgres::{PgDatabaseError, PgErrorPosition, PgSeverity}; +use sqlx::{postgres::Postgres, Connection, Executor, Row, PgPool}; use sqlx_test::new; +use std::time::Duration; #[sqlx_macros::test] async fn it_connects() -> anyhow::Result<()> { @@ -101,12 +102,7 @@ CREATE TEMPORARY TABLE users (id INTEGER PRIMARY KEY); #[sqlx_macros::test] async fn it_executes_with_pool() -> anyhow::Result<()> { - let pool: PgPool = PgPool::builder() - .min_size(2) - .max_size(2) - .test_on_acquire(false) - .build(&dotenv::var("DATABASE_URL")?) - .await?; + let pool = sqlx_test::pool::().await?; let rows = pool.fetch_all("SELECT 1; SElECT 2").await?; @@ -148,6 +144,40 @@ async fn it_can_return_interleaved_nulls_issue_104() -> anyhow::Result<()> { Ok(()) } +#[sqlx_macros::test] +async fn it_can_fail_and_recover() -> anyhow::Result<()> { + let mut conn = new::().await?; + + for i in 0..10 { + // make a query that will fail + let res = conn.execute("INSERT INTO not_found (column) VALUES (10)").await; + assert!(res.is_err()); + + // now try and use the connection + let val: i32 = conn.fetch_one(&*format!("SELECT {}::int4", i)).await?.get(0); + assert_eq!(val, i); + } + + Ok(()) +} + +#[sqlx_macros::test] +async fn it_can_fail_and_recover_with_pool() -> anyhow::Result<()> { + let pool = sqlx_test::pool::().await?; + + for i in 0..10 { + // make a query that will fail + let res = pool.execute("INSERT INTO not_found (column) VALUES (10)").await; + assert!(res.is_err()); + + // now try and use the connection + let val: i32 = pool.fetch_one(&*format!("SELECT {}::int4", i)).await?.get(0); + assert_eq!(val, i); + } + + Ok(()) +} + #[sqlx_macros::test] async fn it_can_query_scalar() -> anyhow::Result<()> { let mut conn = new::().await?; @@ -310,64 +340,63 @@ async fn it_can_work_with_nested_transactions() -> anyhow::Result<()> { Ok(()) } -// // run with `cargo test --features postgres -- --ignored --nocapture pool_smoke_test` -// #[ignore] -// #[cfg_attr(feature = "runtime-async-std", async_std::test)] -// #[cfg_attr(feature = "runtime-tokio", tokio::test)] -// async fn pool_smoke_test() -> anyhow::Result<()> { -// #[cfg(feature = "runtime-tokio")] -// use tokio::{task::spawn, time::delay_for as sleep, time::timeout}; -// -// #[cfg(feature = "runtime-async-std")] -// use async_std::{future::timeout, task::sleep, task::spawn}; -// -// eprintln!("starting pool"); -// -// let pool = PgPool::builder() -// .connect_timeout(Duration::from_secs(5)) -// .min_size(5) -// .max_size(10) -// .build(&dotenv::var("DATABASE_URL")?) -// .await?; -// -// // spin up more tasks than connections available, and ensure we don't deadlock -// for i in 0..20 { -// let pool = pool.clone(); -// spawn(async move { -// loop { -// if let Err(e) = sqlx::query("select 1 + 1").execute(&pool).await { -// eprintln!("pool task {} dying due to {}", i, e); -// break; -// } -// } -// }); -// } -// -// for _ in 0..5 { -// let pool = pool.clone(); -// spawn(async move { -// while !pool.is_closed() { -// // drop acquire() futures in a hot loop -// // https://github.com/launchbadge/sqlx/issues/83 -// drop(pool.acquire()); -// } -// }); -// } -// -// eprintln!("sleeping for 30 seconds"); -// -// sleep(Duration::from_secs(30)).await; -// -// assert_eq!(pool.size(), 10); -// -// eprintln!("closing pool"); -// -// timeout(Duration::from_secs(30), pool.close()).await?; -// -// eprintln!("pool closed successfully"); -// -// Ok(()) -// } +// run with `cargo test --features postgres -- --ignored --nocapture pool_smoke_test` +#[ignore] +#[sqlx_macros::test] +async fn pool_smoke_test() -> anyhow::Result<()> { + #[cfg(feature = "runtime-tokio")] + use tokio::{task::spawn, time::delay_for as sleep, time::timeout}; + + #[cfg(feature = "runtime-async-std")] + use async_std::{future::timeout, task::sleep, task::spawn}; + + eprintln!("starting pool"); + + let pool = PgPool::builder() + .connect_timeout(Duration::from_secs(30)) + .min_size(5) + .max_size(10) + .build(&dotenv::var("DATABASE_URL")?) + .await?; + + // spin up more tasks than connections available, and ensure we don't deadlock + for i in 0..20 { + let pool = pool.clone(); + spawn(async move { + loop { + if let Err(e) = sqlx::query("select 1 + 1").execute(&pool).await { + eprintln!("pool task {} dying due to {}", i, e); + break; + } + } + }); + } + + for _ in 0..5 { + let pool = pool.clone(); + spawn(async move { + while !pool.is_closed() { + // drop acquire() futures in a hot loop + // https://github.com/launchbadge/sqlx/issues/83 + drop(pool.acquire()); + } + }); + } + + eprintln!("sleeping for 30 seconds"); + + sleep(Duration::from_secs(30)).await; + + assert_eq!(pool.size(), 10); + + eprintln!("closing pool"); + + timeout(Duration::from_secs(30), pool.close()).await?; + + eprintln!("pool closed successfully"); + + Ok(()) +} #[sqlx_macros::test] async fn test_invalid_query() -> anyhow::Result<()> {