Refine Rust architecture boundaries
This commit is contained in:
@@ -6,6 +6,37 @@ mod postgres;
|
||||
|
||||
pub use error::RegistryError;
|
||||
pub use ext::{ExtensionMigration, RegistryExtension, apply_extension_migrations};
|
||||
|
||||
pub mod records {
|
||||
pub use crate::model::{
|
||||
AgentSummary, AgentVersionRecord, AuthUserRecord, DescriptorKind, DescriptorMetadata,
|
||||
InvitationRecord, InvocationLogRecord, MembershipRecord, OperationAgentRef,
|
||||
OperationSampleMetadata, OperationSummary, OperationUsageSummary, OperationVersionRecord,
|
||||
Page, PlatformApiKeyRecord, PublishedAgentTool, RegistryOperation, SampleKind,
|
||||
SecretRecord, SecretVersionRecord, SessionRecord, UsageAgentBreakdown, UsageBucket,
|
||||
UsageOperationBreakdown, UsageRollupRecord, UsageSummary, UsageTimelinePoint,
|
||||
WorkspaceMembershipRecord, WorkspaceRecord, WorkspaceUpstream, WorkspaceUpstreamId,
|
||||
YamlImportJob, YamlImportJobCompletion, YamlImportJobId, YamlImportJobStatus,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod requests {
|
||||
pub use crate::model::{
|
||||
CreateAgentDraftVersionRequest, CreateAgentRequest, CreateInvitationRequest,
|
||||
CreateInvocationLogRequest, CreatePlatformApiKeyRequest, CreateSecretRequest,
|
||||
CreateVersionRequest, CreateWorkspaceRequest, CreateYamlImportJobRequest,
|
||||
ListInvocationLogsQuery, PublishAgentRequest, PublishRequest, RotateSecretRequest,
|
||||
SaveAgentBindingsRequest, SaveAuthProfileRequest, SaveDescriptorMetadataRequest,
|
||||
SaveSampleMetadataRequest, SaveWorkspaceUpstreamRequest, UpdateWorkspaceRequest,
|
||||
UsageQuery,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod infrastructure {
|
||||
pub use crate::ext::{ExtensionMigration, RegistryExtension, apply_extension_migrations};
|
||||
pub use crate::postgres::{PostgresPoolConfig, PostgresPoolConfigError, PostgresRegistry};
|
||||
}
|
||||
|
||||
pub use model::{
|
||||
AgentSummary, AgentVersionRecord, AuthUserRecord, CreateAgentDraftVersionRequest,
|
||||
CreateAgentRequest, CreateInvitationRequest, CreateInvocationLogRequest,
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use sqlx::{
|
||||
PgPool,
|
||||
postgres::{PgConnectOptions, PgPoolOptions},
|
||||
};
|
||||
|
||||
use crate::{error::RegistryError, migrations};
|
||||
|
||||
use super::{PostgresPoolConfig, PostgresRegistry};
|
||||
|
||||
impl PostgresRegistry {
|
||||
pub async fn connect(database_url: &str) -> Result<Self, RegistryError> {
|
||||
Self::connect_in_schema(database_url, None).await
|
||||
}
|
||||
|
||||
pub async fn connect_with_options(
|
||||
connect_options: PgConnectOptions,
|
||||
) -> Result<Self, RegistryError> {
|
||||
Self::connect_with_options_and_pool_config(connect_options, PostgresPoolConfig::default())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn connect_with_options_and_pool_config(
|
||||
connect_options: PgConnectOptions,
|
||||
pool_config: PostgresPoolConfig,
|
||||
) -> Result<Self, RegistryError> {
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(pool_config.max_connections)
|
||||
.min_connections(pool_config.min_connections)
|
||||
.acquire_timeout(Duration::from_millis(pool_config.acquire_timeout_ms))
|
||||
.idle_timeout(Duration::from_millis(pool_config.idle_timeout_ms))
|
||||
.max_lifetime(Duration::from_millis(pool_config.max_lifetime_ms))
|
||||
.connect_with(connect_options)
|
||||
.await?;
|
||||
migrations::apply_postgres(&pool).await?;
|
||||
Ok(Self { pool })
|
||||
}
|
||||
|
||||
pub fn pool(&self) -> &PgPool {
|
||||
&self.pool
|
||||
}
|
||||
|
||||
pub async fn migrate(&self) -> Result<(), RegistryError> {
|
||||
migrations::apply_postgres(&self.pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn connect_in_schema(
|
||||
database_url: &str,
|
||||
schema: Option<&str>,
|
||||
) -> Result<Self, RegistryError> {
|
||||
let pool = PgPoolOptions::new()
|
||||
.min_connections(0)
|
||||
.max_connections(1)
|
||||
.idle_timeout(std::time::Duration::from_secs(1))
|
||||
.connect(database_url)
|
||||
.await?;
|
||||
|
||||
if let Some(schema) = schema {
|
||||
sqlx::query(&format!("set search_path to {schema}"))
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let registry = Self { pool };
|
||||
registry.migrate().await?;
|
||||
Ok(registry)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
mod agent;
|
||||
mod api_key;
|
||||
mod auth;
|
||||
mod connection;
|
||||
mod observability;
|
||||
mod operation;
|
||||
mod pool_config;
|
||||
mod secret;
|
||||
mod upstream;
|
||||
mod workspace;
|
||||
@@ -16,18 +18,13 @@ use crank_core::{
|
||||
};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use serde_json::Value;
|
||||
use sqlx::{
|
||||
PgPool, Postgres, Row, Transaction,
|
||||
postgres::{PgConnectOptions, PgPoolOptions, PgRow},
|
||||
types::Json,
|
||||
};
|
||||
use std::{collections::BTreeMap, env, time::Duration};
|
||||
use thiserror::Error;
|
||||
use sqlx::{PgPool, Postgres, Row, Transaction, postgres::PgRow, types::Json};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
pub use pool_config::{PostgresPoolConfig, PostgresPoolConfigError};
|
||||
|
||||
use crate::{
|
||||
error::RegistryError,
|
||||
migrations,
|
||||
model::{
|
||||
AgentSummary, AgentVersionRecord, AuthUserRecord, CreateAgentDraftVersionRequest,
|
||||
CreateAgentRequest, CreateInvitationRequest, CreateInvocationLogRequest,
|
||||
@@ -50,156 +47,7 @@ pub struct PostgresRegistry {
|
||||
pool: PgPool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct PostgresPoolConfig {
|
||||
pub max_connections: u32,
|
||||
pub min_connections: u32,
|
||||
pub acquire_timeout_ms: u64,
|
||||
pub idle_timeout_ms: u64,
|
||||
pub max_lifetime_ms: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
pub enum PostgresPoolConfigError {
|
||||
#[error("invalid postgres pool setting {name}={value}")]
|
||||
InvalidValue { name: &'static str, value: String },
|
||||
#[error("POSTGRES_MAX_CONNECTIONS must be greater than zero")]
|
||||
ZeroMaxConnections,
|
||||
#[error("POSTGRES_MIN_CONNECTIONS must not exceed POSTGRES_MAX_CONNECTIONS")]
|
||||
MinConnectionsExceedMax,
|
||||
}
|
||||
|
||||
impl Default for PostgresPoolConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_connections: 20,
|
||||
min_connections: 2,
|
||||
acquire_timeout_ms: 5_000,
|
||||
idle_timeout_ms: 600_000,
|
||||
max_lifetime_ms: 1_800_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PostgresPoolConfig {
|
||||
pub fn from_env() -> Result<Self, PostgresPoolConfigError> {
|
||||
Self::from_vars(env::vars())
|
||||
}
|
||||
|
||||
fn from_vars<I, K, V>(vars: I) -> Result<Self, PostgresPoolConfigError>
|
||||
where
|
||||
I: IntoIterator<Item = (K, V)>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let vars = vars
|
||||
.into_iter()
|
||||
.map(|(name, value)| (name.as_ref().to_owned(), value.as_ref().to_owned()))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
let defaults = Self::default();
|
||||
let config = Self {
|
||||
max_connections: parse_u32_setting(
|
||||
&vars,
|
||||
"POSTGRES_MAX_CONNECTIONS",
|
||||
defaults.max_connections,
|
||||
)?,
|
||||
min_connections: parse_u32_setting(
|
||||
&vars,
|
||||
"POSTGRES_MIN_CONNECTIONS",
|
||||
defaults.min_connections,
|
||||
)?,
|
||||
acquire_timeout_ms: parse_u64_setting(
|
||||
&vars,
|
||||
"POSTGRES_ACQUIRE_TIMEOUT_MS",
|
||||
defaults.acquire_timeout_ms,
|
||||
)?,
|
||||
idle_timeout_ms: parse_u64_setting(
|
||||
&vars,
|
||||
"POSTGRES_IDLE_TIMEOUT_MS",
|
||||
defaults.idle_timeout_ms,
|
||||
)?,
|
||||
max_lifetime_ms: parse_u64_setting(
|
||||
&vars,
|
||||
"POSTGRES_MAX_LIFETIME_MS",
|
||||
defaults.max_lifetime_ms,
|
||||
)?,
|
||||
};
|
||||
|
||||
config.validate()?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn validate(self) -> Result<Self, PostgresPoolConfigError> {
|
||||
if self.max_connections == 0 {
|
||||
return Err(PostgresPoolConfigError::ZeroMaxConnections);
|
||||
}
|
||||
if self.min_connections > self.max_connections {
|
||||
return Err(PostgresPoolConfigError::MinConnectionsExceedMax);
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PostgresRegistry {
|
||||
pub async fn connect(database_url: &str) -> Result<Self, RegistryError> {
|
||||
Self::connect_in_schema(database_url, None).await
|
||||
}
|
||||
|
||||
pub async fn connect_with_options(
|
||||
connect_options: PgConnectOptions,
|
||||
) -> Result<Self, RegistryError> {
|
||||
Self::connect_with_options_and_pool_config(connect_options, PostgresPoolConfig::default())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn connect_with_options_and_pool_config(
|
||||
connect_options: PgConnectOptions,
|
||||
pool_config: PostgresPoolConfig,
|
||||
) -> Result<Self, RegistryError> {
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(pool_config.max_connections)
|
||||
.min_connections(pool_config.min_connections)
|
||||
.acquire_timeout(Duration::from_millis(pool_config.acquire_timeout_ms))
|
||||
.idle_timeout(Duration::from_millis(pool_config.idle_timeout_ms))
|
||||
.max_lifetime(Duration::from_millis(pool_config.max_lifetime_ms))
|
||||
.connect_with(connect_options)
|
||||
.await?;
|
||||
migrations::apply_postgres(&pool).await?;
|
||||
Ok(Self { pool })
|
||||
}
|
||||
|
||||
pub fn pool(&self) -> &PgPool {
|
||||
&self.pool
|
||||
}
|
||||
|
||||
pub async fn migrate(&self) -> Result<(), RegistryError> {
|
||||
migrations::apply_postgres(&self.pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn connect_in_schema(
|
||||
database_url: &str,
|
||||
schema: Option<&str>,
|
||||
) -> Result<Self, RegistryError> {
|
||||
let pool = PgPoolOptions::new()
|
||||
.min_connections(0)
|
||||
.max_connections(1)
|
||||
.idle_timeout(std::time::Duration::from_secs(1))
|
||||
.connect(database_url)
|
||||
.await?;
|
||||
|
||||
if let Some(schema) = schema {
|
||||
sqlx::query(&format!("set search_path to {schema}"))
|
||||
.execute(&pool)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let registry = Self { pool };
|
||||
registry.migrate().await?;
|
||||
Ok(registry)
|
||||
}
|
||||
|
||||
async fn list_agent_bindings(
|
||||
&self,
|
||||
agent_id: &AgentId,
|
||||
@@ -226,42 +74,6 @@ impl PostgresRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_u32_setting(
|
||||
vars: &BTreeMap<String, String>,
|
||||
name: &'static str,
|
||||
default: u32,
|
||||
) -> Result<u32, PostgresPoolConfigError> {
|
||||
vars.get(name)
|
||||
.map(|value| {
|
||||
value
|
||||
.parse::<u32>()
|
||||
.map_err(|_| PostgresPoolConfigError::InvalidValue {
|
||||
name,
|
||||
value: value.clone(),
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
.map(|value| value.unwrap_or(default))
|
||||
}
|
||||
|
||||
fn parse_u64_setting(
|
||||
vars: &BTreeMap<String, String>,
|
||||
name: &'static str,
|
||||
default: u64,
|
||||
) -> Result<u64, PostgresPoolConfigError> {
|
||||
vars.get(name)
|
||||
.map(|value| {
|
||||
value
|
||||
.parse::<u64>()
|
||||
.map_err(|_| PostgresPoolConfigError::InvalidValue {
|
||||
name,
|
||||
value: value.clone(),
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
.map(|value| value.unwrap_or(default))
|
||||
}
|
||||
|
||||
async fn insert_version_row(
|
||||
tx: &mut Transaction<'_, Postgres>,
|
||||
snapshot: &RegistryOperation,
|
||||
@@ -930,79 +742,3 @@ fn usage_bucket_sql(bucket: crate::model::UsageBucket) -> &'static str {
|
||||
crate::model::UsageBucket::Month => "month",
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod pool_config_tests {
|
||||
use super::{PostgresPoolConfig, PostgresPoolConfigError};
|
||||
|
||||
#[test]
|
||||
fn pool_config_uses_explicit_defaults() {
|
||||
let config = PostgresPoolConfig::from_vars(std::iter::empty::<(&str, &str)>()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config,
|
||||
PostgresPoolConfig {
|
||||
max_connections: 20,
|
||||
min_connections: 2,
|
||||
acquire_timeout_ms: 5_000,
|
||||
idle_timeout_ms: 600_000,
|
||||
max_lifetime_ms: 1_800_000,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_config_parses_overrides() {
|
||||
let config = PostgresPoolConfig::from_vars([
|
||||
("POSTGRES_MAX_CONNECTIONS", "32"),
|
||||
("POSTGRES_MIN_CONNECTIONS", "4"),
|
||||
("POSTGRES_ACQUIRE_TIMEOUT_MS", "7000"),
|
||||
("POSTGRES_IDLE_TIMEOUT_MS", "900000"),
|
||||
("POSTGRES_MAX_LIFETIME_MS", "3600000"),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config,
|
||||
PostgresPoolConfig {
|
||||
max_connections: 32,
|
||||
min_connections: 4,
|
||||
acquire_timeout_ms: 7_000,
|
||||
idle_timeout_ms: 900_000,
|
||||
max_lifetime_ms: 3_600_000,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_config_rejects_invalid_numeric_value() {
|
||||
let error =
|
||||
PostgresPoolConfig::from_vars([("POSTGRES_MAX_CONNECTIONS", "abc")]).unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
error,
|
||||
PostgresPoolConfigError::InvalidValue {
|
||||
name: "POSTGRES_MAX_CONNECTIONS",
|
||||
value: "abc".to_owned(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_config_rejects_zero_max_connections() {
|
||||
let error = PostgresPoolConfig::from_vars([("POSTGRES_MAX_CONNECTIONS", "0")]).unwrap_err();
|
||||
|
||||
assert_eq!(error, PostgresPoolConfigError::ZeroMaxConnections);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_config_rejects_min_connections_above_max() {
|
||||
let error = PostgresPoolConfig::from_vars([
|
||||
("POSTGRES_MAX_CONNECTIONS", "2"),
|
||||
("POSTGRES_MIN_CONNECTIONS", "3"),
|
||||
])
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(error, PostgresPoolConfigError::MinConnectionsExceedMax);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
use std::{collections::BTreeMap, env};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct PostgresPoolConfig {
|
||||
pub max_connections: u32,
|
||||
pub min_connections: u32,
|
||||
pub acquire_timeout_ms: u64,
|
||||
pub idle_timeout_ms: u64,
|
||||
pub max_lifetime_ms: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
pub enum PostgresPoolConfigError {
|
||||
#[error("invalid postgres pool setting {name}={value}")]
|
||||
InvalidValue { name: &'static str, value: String },
|
||||
#[error("POSTGRES_MAX_CONNECTIONS must be greater than zero")]
|
||||
ZeroMaxConnections,
|
||||
#[error("POSTGRES_MIN_CONNECTIONS must not exceed POSTGRES_MAX_CONNECTIONS")]
|
||||
MinConnectionsExceedMax,
|
||||
}
|
||||
|
||||
impl Default for PostgresPoolConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_connections: 20,
|
||||
min_connections: 2,
|
||||
acquire_timeout_ms: 5_000,
|
||||
idle_timeout_ms: 600_000,
|
||||
max_lifetime_ms: 1_800_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PostgresPoolConfig {
|
||||
pub fn from_env() -> Result<Self, PostgresPoolConfigError> {
|
||||
Self::from_vars(env::vars())
|
||||
}
|
||||
|
||||
fn from_vars<I, K, V>(vars: I) -> Result<Self, PostgresPoolConfigError>
|
||||
where
|
||||
I: IntoIterator<Item = (K, V)>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let vars = vars
|
||||
.into_iter()
|
||||
.map(|(name, value)| (name.as_ref().to_owned(), value.as_ref().to_owned()))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
let defaults = Self::default();
|
||||
let config = Self {
|
||||
max_connections: parse_u32_setting(
|
||||
&vars,
|
||||
"POSTGRES_MAX_CONNECTIONS",
|
||||
defaults.max_connections,
|
||||
)?,
|
||||
min_connections: parse_u32_setting(
|
||||
&vars,
|
||||
"POSTGRES_MIN_CONNECTIONS",
|
||||
defaults.min_connections,
|
||||
)?,
|
||||
acquire_timeout_ms: parse_u64_setting(
|
||||
&vars,
|
||||
"POSTGRES_ACQUIRE_TIMEOUT_MS",
|
||||
defaults.acquire_timeout_ms,
|
||||
)?,
|
||||
idle_timeout_ms: parse_u64_setting(
|
||||
&vars,
|
||||
"POSTGRES_IDLE_TIMEOUT_MS",
|
||||
defaults.idle_timeout_ms,
|
||||
)?,
|
||||
max_lifetime_ms: parse_u64_setting(
|
||||
&vars,
|
||||
"POSTGRES_MAX_LIFETIME_MS",
|
||||
defaults.max_lifetime_ms,
|
||||
)?,
|
||||
};
|
||||
|
||||
config.validate()?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn validate(self) -> Result<Self, PostgresPoolConfigError> {
|
||||
if self.max_connections == 0 {
|
||||
return Err(PostgresPoolConfigError::ZeroMaxConnections);
|
||||
}
|
||||
if self.min_connections > self.max_connections {
|
||||
return Err(PostgresPoolConfigError::MinConnectionsExceedMax);
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_u32_setting(
|
||||
vars: &BTreeMap<String, String>,
|
||||
name: &'static str,
|
||||
default: u32,
|
||||
) -> Result<u32, PostgresPoolConfigError> {
|
||||
vars.get(name)
|
||||
.map(|value| {
|
||||
value
|
||||
.parse::<u32>()
|
||||
.map_err(|_| PostgresPoolConfigError::InvalidValue {
|
||||
name,
|
||||
value: value.clone(),
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
.map(|value| value.unwrap_or(default))
|
||||
}
|
||||
|
||||
fn parse_u64_setting(
|
||||
vars: &BTreeMap<String, String>,
|
||||
name: &'static str,
|
||||
default: u64,
|
||||
) -> Result<u64, PostgresPoolConfigError> {
|
||||
vars.get(name)
|
||||
.map(|value| {
|
||||
value
|
||||
.parse::<u64>()
|
||||
.map_err(|_| PostgresPoolConfigError::InvalidValue {
|
||||
name,
|
||||
value: value.clone(),
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
.map(|value| value.unwrap_or(default))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod pool_config_tests {
|
||||
use super::{PostgresPoolConfig, PostgresPoolConfigError};
|
||||
|
||||
#[test]
|
||||
fn pool_config_uses_explicit_defaults() {
|
||||
let config = PostgresPoolConfig::from_vars(std::iter::empty::<(&str, &str)>()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config,
|
||||
PostgresPoolConfig {
|
||||
max_connections: 20,
|
||||
min_connections: 2,
|
||||
acquire_timeout_ms: 5_000,
|
||||
idle_timeout_ms: 600_000,
|
||||
max_lifetime_ms: 1_800_000,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_config_parses_overrides() {
|
||||
let config = PostgresPoolConfig::from_vars([
|
||||
("POSTGRES_MAX_CONNECTIONS", "32"),
|
||||
("POSTGRES_MIN_CONNECTIONS", "4"),
|
||||
("POSTGRES_ACQUIRE_TIMEOUT_MS", "7000"),
|
||||
("POSTGRES_IDLE_TIMEOUT_MS", "900000"),
|
||||
("POSTGRES_MAX_LIFETIME_MS", "3600000"),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config,
|
||||
PostgresPoolConfig {
|
||||
max_connections: 32,
|
||||
min_connections: 4,
|
||||
acquire_timeout_ms: 7_000,
|
||||
idle_timeout_ms: 900_000,
|
||||
max_lifetime_ms: 3_600_000,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_config_rejects_invalid_numeric_value() {
|
||||
let error =
|
||||
PostgresPoolConfig::from_vars([("POSTGRES_MAX_CONNECTIONS", "abc")]).unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
error,
|
||||
PostgresPoolConfigError::InvalidValue {
|
||||
name: "POSTGRES_MAX_CONNECTIONS",
|
||||
value: "abc".to_owned(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_config_rejects_zero_max_connections() {
|
||||
let error = PostgresPoolConfig::from_vars([("POSTGRES_MAX_CONNECTIONS", "0")]).unwrap_err();
|
||||
|
||||
assert_eq!(error, PostgresPoolConfigError::ZeroMaxConnections);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_config_rejects_min_connections_above_max() {
|
||||
let error = PostgresPoolConfig::from_vars([
|
||||
("POSTGRES_MAX_CONNECTIONS", "2"),
|
||||
("POSTGRES_MIN_CONNECTIONS", "3"),
|
||||
])
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(error, PostgresPoolConfigError::MinConnectionsExceedMax);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user