diff --git a/databases.json b/databases.json index 70eface..351c8b0 100644 --- a/databases.json +++ b/databases.json @@ -3,7 +3,7 @@ { "name": "Test database 1 - PostgreSQL", "database": "devdb", - "type": "postgresql", + "type": "postgresql-cluster", "username": "devuser", "password": "changeme", "port": 5432, diff --git a/src/domain/postgres/cluster/restore.rs b/src/domain/postgres/cluster/restore.rs index e24efa8..42bdadd 100644 --- a/src/domain/postgres/cluster/restore.rs +++ b/src/domain/postgres/cluster/restore.rs @@ -5,7 +5,7 @@ use std::process::Command; use std::sync::Arc; use std::time::Instant; -use super::super::connection::{is_superuser, psql_binary_name, select_pg_path, server_version}; +use super::super::connection::{is_superuser, psql_binary_name, select_pg_path, server_version, terminate_all_connections}; use crate::services::backup::logger::JobLogger; use crate::services::config::DatabaseConfig; @@ -40,6 +40,12 @@ pub async fn run( let psql = select_pg_path(&version).join(psql_binary_name()); + if let Err(e) = futures::executor::block_on(terminate_all_connections(&cfg)) { + logger.log("error", format!("Failed to terminate connections for cluster {}: {:?}", cfg.name, e)); + return Err(e.into()); + } + logger.log("info", format!("All user database connections terminated for cluster {}", cfg.name)); + logger.log("info", format!("Replaying cluster dump for {} via {:?}", cfg.name, psql)); let start = Instant::now(); diff --git a/src/domain/postgres/connection.rs b/src/domain/postgres/connection.rs index 0b0aebd..f4f52fa 100644 --- a/src/domain/postgres/connection.rs +++ b/src/domain/postgres/connection.rs @@ -144,6 +144,27 @@ pub async fn terminate_connections(cfg: &DatabaseConfig) -> Result<()> { Ok(()) } +pub async fn terminate_all_connections(cfg: &DatabaseConfig) -> Result<()> { + let mut admin = cfg.clone(); + admin.database = "postgres".to_string().into(); + + let client = connect(&admin).await?; + + client + .execute( + r#" + SELECT pg_terminate_backend(pid) + FROM pg_stat_activity + WHERE datname NOT IN ('postgres', 'template0', 'template1') + AND pid <> pg_backend_pid(); + "#, + &[], + ) + .await?; + + Ok(()) +} + pub fn detect_format_from_file(restore_file: &Path) -> PostgresDumpFormat { match restore_file.extension().and_then(|e| e.to_str()) { Some("dump") => PostgresDumpFormat::Fc,