Skip to main content

heliosdb_proxy/pool/
config.rs

1//! Pool Mode Configuration
2//!
3//! Configuration structures for connection pooling modes.
4
5use super::mode::{PoolingMode, PreparedStatementMode};
6use serde::{Deserialize, Serialize};
7use std::time::Duration;
8
9/// Connection pool mode configuration
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct PoolModeConfig {
12    /// Default pooling mode for new connections
13    #[serde(default)]
14    pub default_mode: PoolingMode,
15
16    /// Maximum connections in the pool per node
17    #[serde(default = "default_max_pool_size")]
18    pub max_pool_size: u32,
19
20    /// Minimum idle connections to maintain per node
21    #[serde(default = "default_min_idle")]
22    pub min_idle: u32,
23
24    /// Idle connection timeout (seconds)
25    #[serde(default = "default_idle_timeout_secs")]
26    pub idle_timeout_secs: u64,
27
28    /// Maximum connection lifetime (seconds)
29    #[serde(default = "default_max_lifetime_secs")]
30    pub max_lifetime_secs: u64,
31
32    /// Timeout for acquiring a connection (seconds)
33    #[serde(default = "default_acquire_timeout_secs")]
34    pub acquire_timeout_secs: u64,
35
36    /// SQL to reset connection state when returning to pool
37    #[serde(default = "default_reset_query")]
38    pub reset_query: String,
39
40    /// Prepared statement handling mode
41    #[serde(default)]
42    pub prepared_statement_mode: PreparedStatementMode,
43
44    /// Whether to validate connections on acquire
45    #[serde(default = "default_test_on_acquire")]
46    pub test_on_acquire: bool,
47
48    /// Validation query
49    #[serde(default = "default_validation_query")]
50    pub validation_query: String,
51
52    /// Connection queue timeout when pool is exhausted (seconds)
53    #[serde(default = "default_queue_timeout_secs")]
54    pub queue_timeout_secs: u64,
55
56    /// Maximum queue size when pool is exhausted (0 = unlimited)
57    #[serde(default)]
58    pub max_queue_size: u32,
59}
60
61fn default_max_pool_size() -> u32 {
62    100
63}
64
65fn default_min_idle() -> u32 {
66    10
67}
68
69fn default_idle_timeout_secs() -> u64 {
70    600 // 10 minutes
71}
72
73fn default_max_lifetime_secs() -> u64 {
74    3600 // 1 hour
75}
76
77fn default_acquire_timeout_secs() -> u64 {
78    5
79}
80
81fn default_reset_query() -> String {
82    "DISCARD ALL".to_string()
83}
84
85fn default_test_on_acquire() -> bool {
86    true
87}
88
89fn default_validation_query() -> String {
90    "SELECT 1".to_string()
91}
92
93fn default_queue_timeout_secs() -> u64 {
94    30
95}
96
97impl Default for PoolModeConfig {
98    fn default() -> Self {
99        Self {
100            default_mode: PoolingMode::default(),
101            max_pool_size: default_max_pool_size(),
102            min_idle: default_min_idle(),
103            idle_timeout_secs: default_idle_timeout_secs(),
104            max_lifetime_secs: default_max_lifetime_secs(),
105            acquire_timeout_secs: default_acquire_timeout_secs(),
106            reset_query: default_reset_query(),
107            prepared_statement_mode: PreparedStatementMode::default(),
108            test_on_acquire: default_test_on_acquire(),
109            validation_query: default_validation_query(),
110            queue_timeout_secs: default_queue_timeout_secs(),
111            max_queue_size: 0,
112        }
113    }
114}
115
116impl PoolModeConfig {
117    /// Create config for session mode (safest defaults)
118    pub fn session_mode() -> Self {
119        Self {
120            default_mode: PoolingMode::Session,
121            prepared_statement_mode: PreparedStatementMode::Named,
122            ..Default::default()
123        }
124    }
125
126    /// Create config for transaction mode (balanced)
127    pub fn transaction_mode() -> Self {
128        Self {
129            default_mode: PoolingMode::Transaction,
130            prepared_statement_mode: PreparedStatementMode::Track,
131            ..Default::default()
132        }
133    }
134
135    /// Create config for statement mode (most aggressive)
136    pub fn statement_mode() -> Self {
137        Self {
138            default_mode: PoolingMode::Statement,
139            prepared_statement_mode: PreparedStatementMode::Disable,
140            ..Default::default()
141        }
142    }
143
144    /// Get idle timeout as Duration
145    pub fn idle_timeout(&self) -> Duration {
146        Duration::from_secs(self.idle_timeout_secs)
147    }
148
149    /// Get max lifetime as Duration
150    pub fn max_lifetime(&self) -> Duration {
151        Duration::from_secs(self.max_lifetime_secs)
152    }
153
154    /// Get acquire timeout as Duration
155    pub fn acquire_timeout(&self) -> Duration {
156        Duration::from_secs(self.acquire_timeout_secs)
157    }
158
159    /// Get queue timeout as Duration
160    pub fn queue_timeout(&self) -> Duration {
161        Duration::from_secs(self.queue_timeout_secs)
162    }
163
164    /// Validate configuration
165    pub fn validate(&self) -> Result<(), String> {
166        if self.max_pool_size == 0 {
167            return Err("max_pool_size must be > 0".to_string());
168        }
169
170        if self.min_idle > self.max_pool_size {
171            return Err("min_idle cannot exceed max_pool_size".to_string());
172        }
173
174        if self.acquire_timeout_secs == 0 {
175            return Err("acquire_timeout_secs must be > 0".to_string());
176        }
177
178        if self.reset_query.is_empty() {
179            return Err("reset_query cannot be empty".to_string());
180        }
181
182        // Statement mode cannot use named prepared statements
183        if self.default_mode == PoolingMode::Statement
184            && self.prepared_statement_mode == PreparedStatementMode::Named
185        {
186            return Err(
187                "Statement mode cannot use named prepared statements".to_string()
188            );
189        }
190
191        Ok(())
192    }
193
194    /// Apply overrides from another config (for merging)
195    pub fn merge(&mut self, other: &PoolModeConfig) {
196        // Only override non-default values
197        // This is a simplified merge - in practice you'd want Option<T> fields
198        if other.max_pool_size != default_max_pool_size() {
199            self.max_pool_size = other.max_pool_size;
200        }
201        if other.min_idle != default_min_idle() {
202            self.min_idle = other.min_idle;
203        }
204        if other.idle_timeout_secs != default_idle_timeout_secs() {
205            self.idle_timeout_secs = other.idle_timeout_secs;
206        }
207        if other.reset_query != default_reset_query() {
208            self.reset_query = other.reset_query.clone();
209        }
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216
217    #[test]
218    fn test_default_config() {
219        let config = PoolModeConfig::default();
220        assert_eq!(config.default_mode, PoolingMode::Session);
221        assert_eq!(config.max_pool_size, 100);
222        assert_eq!(config.min_idle, 10);
223        assert_eq!(config.reset_query, "DISCARD ALL");
224        assert!(config.validate().is_ok());
225    }
226
227    #[test]
228    fn test_session_mode_config() {
229        let config = PoolModeConfig::session_mode();
230        assert_eq!(config.default_mode, PoolingMode::Session);
231        assert_eq!(config.prepared_statement_mode, PreparedStatementMode::Named);
232    }
233
234    #[test]
235    fn test_transaction_mode_config() {
236        let config = PoolModeConfig::transaction_mode();
237        assert_eq!(config.default_mode, PoolingMode::Transaction);
238        assert_eq!(config.prepared_statement_mode, PreparedStatementMode::Track);
239    }
240
241    #[test]
242    fn test_statement_mode_config() {
243        let config = PoolModeConfig::statement_mode();
244        assert_eq!(config.default_mode, PoolingMode::Statement);
245        assert_eq!(
246            config.prepared_statement_mode,
247            PreparedStatementMode::Disable
248        );
249    }
250
251    #[test]
252    fn test_validation() {
253        let mut config = PoolModeConfig::default();
254
255        // Valid config
256        assert!(config.validate().is_ok());
257
258        // Invalid: max_pool_size = 0
259        config.max_pool_size = 0;
260        assert!(config.validate().is_err());
261        config.max_pool_size = 100;
262
263        // Invalid: min_idle > max_pool_size
264        config.min_idle = 200;
265        assert!(config.validate().is_err());
266        config.min_idle = 10;
267
268        // Invalid: statement mode with named prepared statements
269        config.default_mode = PoolingMode::Statement;
270        config.prepared_statement_mode = PreparedStatementMode::Named;
271        assert!(config.validate().is_err());
272    }
273
274    #[test]
275    fn test_durations() {
276        let config = PoolModeConfig::default();
277        assert_eq!(config.idle_timeout(), Duration::from_secs(600));
278        assert_eq!(config.max_lifetime(), Duration::from_secs(3600));
279        assert_eq!(config.acquire_timeout(), Duration::from_secs(5));
280    }
281}