feat: complete Epic 1 production foundation
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
use crank_config::DatabaseSettings;
|
||||
use crank_registry::{PostgresPoolConfig, PostgresRegistry, RegistryError};
|
||||
use sqlx::{
|
||||
PgPool,
|
||||
postgres::{PgConnectOptions, PgPoolOptions},
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
use super::CliError;
|
||||
|
||||
pub(super) async fn connect(config: &DatabaseSettings) -> Result<PgPool, CliError> {
|
||||
let options = connect_options(config)?;
|
||||
for attempt in 1..=10 {
|
||||
let result = PgPoolOptions::new()
|
||||
.max_connections(config.pool.max_connections)
|
||||
.min_connections(config.pool.min_connections)
|
||||
.acquire_timeout(Duration::from_millis(config.pool.acquire_timeout_ms))
|
||||
.idle_timeout(Duration::from_millis(config.pool.idle_timeout_ms))
|
||||
.max_lifetime(Duration::from_millis(config.pool.max_lifetime_ms))
|
||||
.connect_with(options.clone())
|
||||
.await;
|
||||
match result {
|
||||
Ok(pool) => return Ok(pool),
|
||||
Err(_) if attempt < 10 => tokio::time::sleep(Duration::from_secs(1)).await,
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
Err(CliError::new(
|
||||
"storage_unavailable",
|
||||
"database.connect",
|
||||
"contact_operator",
|
||||
))
|
||||
}
|
||||
|
||||
pub(super) async fn connect_registry(
|
||||
config: &DatabaseSettings,
|
||||
) -> Result<PostgresRegistry, CliError> {
|
||||
let options = connect_options(config)?;
|
||||
let pool_config = PostgresPoolConfig {
|
||||
max_connections: config.pool.max_connections,
|
||||
min_connections: config.pool.min_connections,
|
||||
acquire_timeout_ms: config.pool.acquire_timeout_ms,
|
||||
idle_timeout_ms: config.pool.idle_timeout_ms,
|
||||
max_lifetime_ms: config.pool.max_lifetime_ms,
|
||||
};
|
||||
for attempt in 1..=10 {
|
||||
let result =
|
||||
PostgresRegistry::connect_with_options_and_pool_config(options.clone(), pool_config)
|
||||
.await;
|
||||
match result {
|
||||
Ok(registry) => return Ok(registry),
|
||||
Err(RegistryError::Storage(_)) if attempt < 10 => {
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
Err(error) => return Err(CliError::from_registry(error)),
|
||||
}
|
||||
}
|
||||
Err(CliError::new(
|
||||
"storage_unavailable",
|
||||
"database.connect",
|
||||
"contact_operator",
|
||||
))
|
||||
}
|
||||
|
||||
fn connect_options(config: &DatabaseSettings) -> Result<PgConnectOptions, CliError> {
|
||||
if let Some(url) = &config.url {
|
||||
url.expose_secret()
|
||||
.parse::<PgConnectOptions>()
|
||||
.map_err(|_| CliError::new("config_invalid", "database.source", "run_preflight"))
|
||||
} else {
|
||||
Ok(PgConnectOptions::new()
|
||||
.host(&config.host)
|
||||
.port(config.port)
|
||||
.database(&config.database)
|
||||
.username(&config.username)
|
||||
.password(config.password.expose_secret()))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user