Skip to content

Commit e48881d

Browse files
committed
Fix database_is_empty() checking wrong database and add timeout
- Connect to target_db_url instead of target_url to check if correct database is empty (was checking default db, not target db) - Add 30-second timeout to prevent indefinite hang on stale serverless connections (SerenDB connection pool exhaustion) Fixes #25 Fixes #26
1 parent d080724 commit e48881d

2 files changed

Lines changed: 21 additions & 7 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "database-replicator"
3-
version = "5.3.18"
3+
version = "5.3.19"
44
edition = "2021"
55
license = "Apache-2.0"
66
description = "Universal database-to-PostgreSQL replication CLI. Supports PostgreSQL, SQLite, MongoDB, and MySQL."

src/commands/init.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,15 @@ pub async fn init(
405405
db_info.name
406406
);
407407

408-
// Check if empty (reuse existing connection to avoid pool exhaustion)
409-
if database_is_empty(&target_client).await? {
408+
// Check if empty by connecting to the SPECIFIC database
409+
// (target_client is connected to default db, not the target db)
410+
let is_empty = {
411+
let db_client =
412+
postgres::connect_with_retry(&target_db_url).await?;
413+
database_is_empty(&db_client).await?
414+
}; // db_client dropped here
415+
416+
if is_empty {
410417
tracing::info!(
411418
" Database '{}' is empty, proceeding with restore",
412419
db_info.name
@@ -676,18 +683,25 @@ fn confirm_replication(sizes: &[migration::DatabaseSizeInfo]) -> Result<bool> {
676683
Ok(input.trim().to_lowercase() == "y")
677684
}
678685

679-
/// Checks if a database is empty (no user tables)
686+
/// Checks if the currently connected database is empty (has no user tables).
680687
///
681-
/// Uses the existing connection to avoid connection pool exhaustion on
682-
/// serverless PostgreSQL providers (SerenDB, Neon) that have strict limits.
688+
/// Includes a 30-second timeout to prevent hanging on stale serverless connections.
683689
async fn database_is_empty(client: &tokio_postgres::Client) -> Result<bool> {
684690
let query = "
685691
SELECT COUNT(*)
686692
FROM information_schema.tables
687693
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
688694
";
689695

690-
let row = client.query_one(query, &[]).await?;
696+
// Add timeout to prevent indefinite hang on stale serverless connections
697+
let row = tokio::time::timeout(
698+
std::time::Duration::from_secs(30),
699+
client.query_one(query, &[]),
700+
)
701+
.await
702+
.context("database_is_empty query timed out after 30 seconds")?
703+
.context("Failed to query information_schema.tables")?;
704+
691705
let count: i64 = row.get(0);
692706

693707
Ok(count == 0)

0 commit comments

Comments
 (0)