chore: Add test for running a query after swapping out a sqlx pool
@@ -44,15 +44,19 @@ where
}
}
- /// Replaces the underlying [`Pool`] with `new_pool`.
+ /// Replaces the underlying [`Pool`] with `new_pool`, returning
+ /// the previous pool
///
/// Existing connections obtained by performing operations on the pool
/// before the call to `replace` are still valid.
///
/// This method affects new operations only.
- pub fn replace(&self, new_pool: Pool<DB>) {
+ pub fn replace(&self, new_pool: Pool<DB>) -> Arc<Pool<DB>> {
+ let mut t = Arc::new(new_pool);
let mut pool = self.pool.write().expect("poisoned");
- *pool = Arc::new(new_pool);
+ // swap the new pool for the old pool
+ std::mem::swap(&mut t, &mut *pool);
+ t
}
}
@@ -287,4 +291,39 @@ mod tests {
assert!(res.is_none());
}
+
+ #[tokio::test]
+ async fn test_replace_with_outstanding_future() {
+ maybe_skip_integration!();
+ println!("tests are running");
+
+ let db = connect_db().await.unwrap();
+ let db = HotSwapPool::new(db);
+
+ sqlx::query("CREATE TABLE IF NOT EXISTS test (id int)")
+ .execute(&db)
+ .await
+ .expect("executed");
+
+ // create a future from the pool but don't execute it yet
+ let query_future = sqlx::query("CREATE TABLE IF NOT EXISTS test (id int)").execute(&db);
+
+ // hot swap a new pool. This should not deadlock
+ let new_pool = connect_db().await.unwrap();
+ let old_pool = db.replace(new_pool);
+
+ // Ensure there are outstanding references to the old pool
+ // captured in the future
+ assert_eq!(Arc::strong_count(&old_pool), 1);
+
+ // resolve the future created prior to the previous
+ // connection (executes against the new connection)
+ query_future.await.expect("executed");
+
+ // can also run queries successfully against the new pool
+ sqlx::query("CREATE TABLE IF NOT EXISTS test (id int)")
+ .execute(&db)
+ .await
+ .expect("executed");
+ }
}
