use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageConfig {
pub postgresql: Option<PostgreSQLConfig>,
pub redis: Option<RedisConfig>,
pub sled: Option<SledConfig>,
pub pool: PoolConfig,
pub retry: RetryConfig,
pub monitoring: MonitoringConfig,
}
impl Default for StorageConfig {
fn default() -> Self {
Self {
postgresql: None,
redis: None,
sled: None,
pool: PoolConfig::default(),
retry: RetryConfig::default(),
monitoring: MonitoringConfig::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PostgreSQLConfig {
pub host: String,
pub port: u16,
pub database: String,
pub username: String,
pub password: String,
pub ssl_mode: SSLMode,
pub connect_timeout: Duration,
pub query_timeout: Duration,
pub enable_migrations: bool,
pub migrations_dir: Option<PathBuf>,
pub schema: String,
pub application_name: String,
}
impl Default for PostgreSQLConfig {
fn default() -> Self {
Self {
host: "localhost".to_string(),
port: 5432,
database: "llm_optimizer".to_string(),
username: "postgres".to_string(),
password: String::new(),
ssl_mode: SSLMode::Prefer,
connect_timeout: Duration::from_secs(10),
query_timeout: Duration::from_secs(30),
enable_migrations: true,
migrations_dir: None,
schema: "public".to_string(),
application_name: "llm-auto-optimizer".to_string(),
}
}
}
impl PostgreSQLConfig {
pub fn connection_url(&self) -> String {
format!(
"postgresql://{}:{}@{}:{}/{}?application_name={}",
self.username, self.password, self.host, self.port, self.database, self.application_name
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SSLMode {
Disable,
Prefer,
Require,
VerifyCA,
VerifyFull,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RedisConfig {
pub host: String,
pub port: u16,
pub database: u8,
pub password: Option<String>,
pub username: Option<String>,
pub connect_timeout: Duration,
pub command_timeout: Duration,
pub cluster_mode: bool,
pub cluster_nodes: Vec<String>,
pub key_prefix: String,
pub default_ttl: Option<i64>,
pub enable_pubsub: bool,
}
impl Default for RedisConfig {
fn default() -> Self {
Self {
host: "localhost".to_string(),
port: 6379,
database: 0,
password: None,
username: None,
connect_timeout: Duration::from_secs(5),
command_timeout: Duration::from_secs(10),
cluster_mode: false,
cluster_nodes: Vec::new(),
key_prefix: "llm_optimizer:".to_string(),
default_ttl: None,
enable_pubsub: false,
}
}
}
impl RedisConfig {
pub fn connection_url(&self) -> String {
let auth = if let Some(ref username) = self.username {
if let Some(ref password) = self.password {
format!("{}:{}@", username, password)
} else {
format!("{}@", username)
}
} else if let Some(ref password) = self.password {
format!(":{}@", password)
} else {
String::new()
};
format!(
"redis://{}{}:{}/{}",
auth, self.host, self.port, self.database
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SledConfig {
pub path: PathBuf,
pub cache_capacity_bytes: u64,
pub use_compression: bool,
pub flush_every_ms: Option<u64>,
pub mode: SledMode,
pub temporary: bool,
pub create_dir: bool,
}
impl Default for SledConfig {
fn default() -> Self {
Self {
path: PathBuf::from("./data/sled"),
cache_capacity_bytes: 1024 * 1024 * 1024, use_compression: true,
flush_every_ms: Some(1000),
mode: SledMode::HighThroughput,
temporary: false,
create_dir: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SledMode {
HighThroughput,
LowSpace,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PoolConfig {
pub min_connections: u32,
pub max_connections: u32,
pub idle_timeout: Duration,
pub max_lifetime: Duration,
pub acquire_timeout: Duration,
}
impl Default for PoolConfig {
fn default() -> Self {
Self {
min_connections: 2,
max_connections: 10,
idle_timeout: Duration::from_secs(600), max_lifetime: Duration::from_secs(1800), acquire_timeout: Duration::from_secs(30),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetryConfig {
pub enabled: bool,
pub max_attempts: usize,
pub initial_delay: Duration,
pub max_delay: Duration,
pub backoff_multiplier: f64,
pub jitter_factor: f64,
}
impl Default for RetryConfig {
fn default() -> Self {
Self {
enabled: true,
max_attempts: 3,
initial_delay: Duration::from_millis(100),
max_delay: Duration::from_secs(10),
backoff_multiplier: 2.0,
jitter_factor: 0.2,
}
}
}
impl RetryConfig {
pub fn delay_for_attempt(&self, attempt: usize) -> Duration {
if attempt == 0 || !self.enabled {
return Duration::from_secs(0);
}
let delay_ms = self.initial_delay.as_millis() as f64
* self.backoff_multiplier.powi((attempt - 1) as i32);
let delay_ms = delay_ms.min(self.max_delay.as_millis() as f64);
let jitter = delay_ms * self.jitter_factor * (rand::random::<f64>() - 0.5);
let final_delay_ms = (delay_ms + jitter).max(0.0);
Duration::from_millis(final_delay_ms as u64)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MonitoringConfig {
pub enable_metrics: bool,
pub metrics_interval: Duration,
pub enable_health_checks: bool,
pub health_check_interval: Duration,
pub enable_query_logging: bool,
pub slow_query_threshold: Duration,
pub enable_pool_monitoring: bool,
}
impl Default for MonitoringConfig {
fn default() -> Self {
Self {
enable_metrics: true,
metrics_interval: Duration::from_secs(60),
enable_health_checks: true,
health_check_interval: Duration::from_secs(30),
enable_query_logging: true,
slow_query_threshold: Duration::from_millis(1000),
enable_pool_monitoring: true,
}
}
}