use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::time::Duration;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ReplicationRole {
Primary,
Standby,
MultiPrimary,
Coordinator,
Shard,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SyncMode {
Sync,
Async,
SemiSync { min_replicas: u32 },
}
impl Default for SyncMode {
fn default() -> Self {
Self::Async
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConflictStrategy {
LastWriterWins,
FirstWriterWins,
VectorClockPrecedence,
Custom,
ManualReview,
}
impl Default for ConflictStrategy {
fn default() -> Self {
Self::LastWriterWins
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum NodeHealth {
Healthy,
Lagging,
Unreachable,
Recovering,
Failed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TrMode {
None,
Session,
Select,
Transaction,
}
impl Default for TrMode {
fn default() -> Self {
Self::Session
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WarmStandbyConfig {
pub role: ReplicationRole,
pub node_id: Uuid,
pub bind_addr: SocketAddr,
#[serde(skip_serializing_if = "Option::is_none")]
pub primary: Option<PrimaryConfig>,
#[serde(default)]
pub standbys: Vec<StandbyConfig>,
#[serde(default)]
pub wal_streaming: WalStreamingConfig,
#[serde(default)]
pub failover: FailoverConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrimaryConfig {
pub host: String,
pub port: u16,
#[serde(default = "default_connect_timeout")]
pub connect_timeout: Duration,
#[serde(default)]
pub use_tls: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StandbyConfig {
pub node_id: Uuid,
pub host: String,
pub port: u16,
#[serde(default)]
pub sync_mode: SyncMode,
#[serde(default = "default_priority")]
pub priority: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WalStreamingConfig {
#[serde(default = "default_wal_segment_size")]
pub segment_size: usize,
#[serde(default = "default_wal_retention")]
pub retention_segments: u32,
#[serde(default = "default_buffer_size")]
pub buffer_size: usize,
#[serde(default = "default_batch_size")]
pub batch_size: usize,
#[serde(default = "default_true")]
pub enable_compression: bool,
#[serde(default = "default_checkpoint_interval")]
pub checkpoint_interval: Duration,
#[serde(default = "default_heartbeat_interval")]
pub heartbeat_interval: Duration,
#[serde(default = "default_heartbeat_timeout")]
pub heartbeat_timeout: Duration,
}
impl Default for WalStreamingConfig {
fn default() -> Self {
Self {
segment_size: default_wal_segment_size(),
retention_segments: default_wal_retention(),
buffer_size: default_buffer_size(),
batch_size: default_batch_size(),
enable_compression: true,
checkpoint_interval: default_checkpoint_interval(),
heartbeat_interval: default_heartbeat_interval(),
heartbeat_timeout: default_heartbeat_timeout(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FailoverConfig {
#[serde(default)]
pub auto_failover: bool,
#[serde(default = "default_health_check_interval")]
pub health_check_interval: Duration,
#[serde(default = "default_failover_threshold")]
pub failover_threshold: u32,
#[serde(default = "default_max_lag")]
pub max_replication_lag: u64,
#[serde(default = "default_failover_timeout")]
pub failover_timeout: Duration,
#[serde(default)]
pub require_confirmation: bool,
}
impl Default for FailoverConfig {
fn default() -> Self {
Self {
auto_failover: false,
health_check_interval: default_health_check_interval(),
failover_threshold: default_failover_threshold(),
max_replication_lag: default_max_lag(),
failover_timeout: default_failover_timeout(),
require_confirmation: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MultiPrimaryConfig {
pub node_id: Uuid,
pub region_id: String,
pub bind_addr: SocketAddr,
pub peers: Vec<PeerConfig>,
#[serde(default)]
pub conflict_strategy: ConflictStrategy,
#[serde(default)]
pub sync: MultiPrimarySyncConfig,
#[serde(default)]
pub branch_sync: BranchSyncConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeerConfig {
pub node_id: Uuid,
pub region_id: String,
pub host: String,
pub port: u16,
#[serde(default)]
pub use_tls: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MultiPrimarySyncConfig {
#[serde(default = "default_sync_interval")]
pub sync_interval: Duration,
#[serde(default = "default_batch_size")]
pub batch_size: usize,
#[serde(default = "default_true")]
pub vector_clocks: bool,
#[serde(default = "default_convergence_timeout")]
pub convergence_timeout: Duration,
}
impl Default for MultiPrimarySyncConfig {
fn default() -> Self {
Self {
sync_interval: default_sync_interval(),
batch_size: default_batch_size(),
vector_clocks: true,
convergence_timeout: default_convergence_timeout(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BranchSyncConfig {
#[serde(default)]
pub branches: Vec<String>,
#[serde(default)]
pub exclude_branches: Vec<String>,
#[serde(default)]
pub sync_mode: SyncMode,
}
impl Default for BranchSyncConfig {
fn default() -> Self {
Self {
branches: Vec::new(),
exclude_branches: Vec::new(),
sync_mode: SyncMode::Async,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShardingConfig {
pub role: ReplicationRole,
pub node_id: Uuid,
pub bind_addr: SocketAddr,
#[serde(skip_serializing_if = "Option::is_none")]
pub shard_id: Option<u32>,
#[serde(default)]
pub hash_ring: HashRingConfig,
#[serde(default)]
pub shards: Vec<ShardNodeConfig>,
#[serde(default)]
pub cross_shard: CrossShardConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HashRingConfig {
#[serde(default = "default_virtual_nodes")]
pub virtual_nodes: u32,
#[serde(default)]
pub hash_function: HashFunction,
#[serde(default = "default_replication_factor")]
pub replication_factor: u32,
}
impl Default for HashRingConfig {
fn default() -> Self {
Self {
virtual_nodes: default_virtual_nodes(),
hash_function: HashFunction::default(),
replication_factor: default_replication_factor(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum HashFunction {
XxHash,
Murmur3,
Blake3,
}
impl Default for HashFunction {
fn default() -> Self {
Self::XxHash
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShardNodeConfig {
pub shard_id: u32,
pub node_id: Uuid,
pub host: String,
pub port: u16,
pub key_range_start: u64,
pub key_range_end: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrossShardConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_query_timeout")]
pub query_timeout: Duration,
#[serde(default = "default_max_parallel")]
pub max_parallel_queries: u32,
#[serde(default)]
pub enable_2pc: bool,
}
impl Default for CrossShardConfig {
fn default() -> Self {
Self {
enabled: true,
query_timeout: default_query_timeout(),
max_parallel_queries: default_max_parallel(),
enable_2pc: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BranchReplicationConfig {
pub targets: Vec<BranchTarget>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BranchTarget {
pub branch: String,
pub remote_host: String,
pub remote_port: u16,
pub auth: AuthMethod,
#[serde(default)]
pub sync_mode: SyncMode,
#[serde(default = "default_max_lag_ms")]
pub max_lag_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AuthMethod {
SecurePairing {
pairing_key: String,
},
Tls {
cert_path: PathBuf,
key_path: PathBuf,
ca_path: PathBuf,
},
Token {
token: String,
},
OAuth2 {
client_id: String,
client_secret: String,
token_endpoint: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HaDedupConfig {
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default = "default_min_dedup_size")]
pub min_blob_size: usize,
#[serde(default)]
pub hash_algorithm: ContentHashAlgorithm,
#[serde(default = "default_true")]
pub lazy_fetch: bool,
#[serde(default = "default_fetch_timeout")]
pub fetch_timeout: Duration,
}
impl Default for HaDedupConfig {
fn default() -> Self {
Self {
enabled: true,
min_blob_size: default_min_dedup_size(),
hash_algorithm: ContentHashAlgorithm::default(),
lazy_fetch: true,
fetch_timeout: default_fetch_timeout(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ContentHashAlgorithm {
Blake3,
Sha256,
}
impl Default for ContentHashAlgorithm {
fn default() -> Self {
Self::Blake3
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProxyConfig {
pub bind_addr: SocketAddr,
pub backends: Vec<BackendConfig>,
#[serde(default)]
pub connection_pool: ConnectionPoolConfig,
#[serde(default)]
pub load_balancer: LoadBalancerConfig,
#[serde(default)]
pub health_check: HealthCheckConfig,
#[serde(default)]
pub tr: TrConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackendConfig {
pub node_id: Uuid,
pub host: String,
pub port: u16,
pub role: ReplicationRole,
#[serde(default = "default_weight")]
pub weight: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConnectionPoolConfig {
#[serde(default = "default_min_connections")]
pub min_connections: u32,
#[serde(default = "default_max_connections")]
pub max_connections: u32,
#[serde(default = "default_idle_timeout")]
pub idle_timeout: Duration,
#[serde(default = "default_acquire_timeout")]
pub acquire_timeout: Duration,
}
impl Default for ConnectionPoolConfig {
fn default() -> Self {
Self {
min_connections: default_min_connections(),
max_connections: default_max_connections(),
idle_timeout: default_idle_timeout(),
acquire_timeout: default_acquire_timeout(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoadBalancerConfig {
#[serde(default)]
pub strategy: LoadBalanceStrategy,
#[serde(default = "default_true")]
pub read_write_split: bool,
#[serde(default)]
pub session_affinity: bool,
}
impl Default for LoadBalancerConfig {
fn default() -> Self {
Self {
strategy: LoadBalanceStrategy::default(),
read_write_split: true,
session_affinity: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum LoadBalanceStrategy {
RoundRobin,
LeastConnections,
Random,
WeightedRoundRobin,
LatencyBased,
}
impl Default for LoadBalanceStrategy {
fn default() -> Self {
Self::RoundRobin
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthCheckConfig {
#[serde(default = "default_health_check_interval")]
pub interval: Duration,
#[serde(default = "default_health_check_timeout")]
pub timeout: Duration,
#[serde(default = "default_unhealthy_threshold")]
pub unhealthy_threshold: u32,
#[serde(default = "default_healthy_threshold")]
pub healthy_threshold: u32,
}
impl Default for HealthCheckConfig {
fn default() -> Self {
Self {
interval: default_health_check_interval(),
timeout: default_health_check_timeout(),
unhealthy_threshold: default_unhealthy_threshold(),
healthy_threshold: default_healthy_threshold(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub default_mode: TrMode,
#[serde(default = "default_journal_retention")]
pub journal_retention: Duration,
#[serde(default = "default_max_journal_size")]
pub max_journal_size: usize,
#[serde(default = "default_true")]
pub restore_cursors: bool,
#[serde(default = "default_true")]
pub migrate_session_state: bool,
}
impl Default for TrConfig {
fn default() -> Self {
Self {
enabled: false,
default_mode: TrMode::default(),
journal_retention: default_journal_retention(),
max_journal_size: default_max_journal_size(),
restore_cursors: true,
migrate_session_state: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AbTestingConfig {
#[serde(default)]
pub enabled: bool,
pub experiments: Vec<ExperimentConfig>,
#[serde(default = "default_branch")]
pub default_branch: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExperimentConfig {
pub name: String,
pub branches: Vec<String>,
pub assignment: AssignmentStrategy,
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default)]
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AssignmentStrategy {
UserIdModulo {
divisor: u32,
threshold: u32,
},
Percentage {
percentages: Vec<u32>,
},
Attribute {
attribute: String,
mapping: HashMap<String, String>,
},
Random {
seed: u64,
},
}
fn default_connect_timeout() -> Duration {
Duration::from_secs(10)
}
fn default_priority() -> u32 {
100
}
fn default_wal_segment_size() -> usize {
16 * 1024 * 1024 }
fn default_wal_retention() -> u32 {
64 }
fn default_buffer_size() -> usize {
1024 * 1024 }
fn default_batch_size() -> usize {
1000
}
fn default_true() -> bool {
true
}
fn default_checkpoint_interval() -> Duration {
Duration::from_secs(300) }
fn default_heartbeat_interval() -> Duration {
Duration::from_secs(1)
}
fn default_heartbeat_timeout() -> Duration {
Duration::from_secs(10)
}
fn default_health_check_interval() -> Duration {
Duration::from_secs(5)
}
fn default_failover_threshold() -> u32 {
3
}
fn default_max_lag() -> u64 {
10_000_000 }
fn default_failover_timeout() -> Duration {
Duration::from_secs(30)
}
fn default_sync_interval() -> Duration {
Duration::from_secs(5)
}
fn default_convergence_timeout() -> Duration {
Duration::from_secs(60)
}
fn default_virtual_nodes() -> u32 {
150
}
fn default_replication_factor() -> u32 {
3
}
fn default_query_timeout() -> Duration {
Duration::from_secs(30)
}
fn default_max_parallel() -> u32 {
10
}
fn default_max_lag_ms() -> u64 {
5000 }
fn default_min_dedup_size() -> usize {
4096 }
fn default_fetch_timeout() -> Duration {
Duration::from_secs(30)
}
fn default_weight() -> u32 {
100
}
fn default_min_connections() -> u32 {
2
}
fn default_max_connections() -> u32 {
100
}
fn default_idle_timeout() -> Duration {
Duration::from_secs(600) }
fn default_acquire_timeout() -> Duration {
Duration::from_secs(30)
}
fn default_health_check_timeout() -> Duration {
Duration::from_secs(5)
}
fn default_unhealthy_threshold() -> u32 {
3
}
fn default_healthy_threshold() -> u32 {
2
}
fn default_journal_retention() -> Duration {
Duration::from_secs(3600) }
fn default_max_journal_size() -> usize {
100 * 1024 * 1024 }
fn default_branch() -> String {
"main".to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sync_mode_default() {
assert_eq!(SyncMode::default(), SyncMode::Async);
}
#[test]
fn test_conflict_strategy_default() {
assert_eq!(ConflictStrategy::default(), ConflictStrategy::LastWriterWins);
}
#[test]
fn test_tr_mode_default() {
assert_eq!(TrMode::default(), TrMode::Session);
}
#[test]
fn test_wal_streaming_config_default() {
let config = WalStreamingConfig::default();
assert_eq!(config.segment_size, 16 * 1024 * 1024);
assert!(config.enable_compression);
}
#[test]
fn test_failover_config_default() {
let config = FailoverConfig::default();
assert!(!config.auto_failover);
assert_eq!(config.failover_threshold, 3);
}
}