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("Statement mode cannot use named prepared statements".to_string());
187        }
188
189        Ok(())
190    }
191
192    /// Apply overrides from another config (for merging)
193    pub fn merge(&mut self, other: &PoolModeConfig) {
194        // Only override non-default values
195        // This is a simplified merge - in practice you'd want Option<T> fields
196        if other.max_pool_size != default_max_pool_size() {
197            self.max_pool_size = other.max_pool_size;
198        }
199        if other.min_idle != default_min_idle() {
200            self.min_idle = other.min_idle;
201        }
202        if other.idle_timeout_secs != default_idle_timeout_secs() {
203            self.idle_timeout_secs = other.idle_timeout_secs;
204        }
205        if other.reset_query != default_reset_query() {
206            self.reset_query = other.reset_query.clone();
207        }
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214
215    #[test]
216    fn test_default_config() {
217        let config = PoolModeConfig::default();
218        assert_eq!(config.default_mode, PoolingMode::Session);
219        assert_eq!(config.max_pool_size, 100);
220        assert_eq!(config.min_idle, 10);
221        assert_eq!(config.reset_query, "DISCARD ALL");
222        assert!(config.validate().is_ok());
223    }
224
225    #[test]
226    fn test_session_mode_config() {
227        let config = PoolModeConfig::session_mode();
228        assert_eq!(config.default_mode, PoolingMode::Session);
229        assert_eq!(config.prepared_statement_mode, PreparedStatementMode::Named);
230    }
231
232    #[test]
233    fn test_transaction_mode_config() {
234        let config = PoolModeConfig::transaction_mode();
235        assert_eq!(config.default_mode, PoolingMode::Transaction);
236        assert_eq!(config.prepared_statement_mode, PreparedStatementMode::Track);
237    }
238
239    #[test]
240    fn test_statement_mode_config() {
241        let config = PoolModeConfig::statement_mode();
242        assert_eq!(config.default_mode, PoolingMode::Statement);
243        assert_eq!(
244            config.prepared_statement_mode,
245            PreparedStatementMode::Disable
246        );
247    }
248
249    #[test]
250    fn test_validation() {
251        let mut config = PoolModeConfig::default();
252
253        // Valid config
254        assert!(config.validate().is_ok());
255
256        // Invalid: max_pool_size = 0
257        config.max_pool_size = 0;
258        assert!(config.validate().is_err());
259        config.max_pool_size = 100;
260
261        // Invalid: min_idle > max_pool_size
262        config.min_idle = 200;
263        assert!(config.validate().is_err());
264        config.min_idle = 10;
265
266        // Invalid: statement mode with named prepared statements
267        config.default_mode = PoolingMode::Statement;
268        config.prepared_statement_mode = PreparedStatementMode::Named;
269        assert!(config.validate().is_err());
270    }
271
272    #[test]
273    fn test_durations() {
274        let config = PoolModeConfig::default();
275        assert_eq!(config.idle_timeout(), Duration::from_secs(600));
276        assert_eq!(config.max_lifetime(), Duration::from_secs(3600));
277        assert_eq!(config.acquire_timeout(), Duration::from_secs(5));
278    }
279}