1use std::sync::Mutex;
7use std::time::{Duration, Instant};
8
9use serde::{Deserialize, Serialize};
10
11use crate::circuit_breaker::{CircuitBreaker, CircuitState, DefaultCircuitBreaker};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct ElasticConfig {
19 pub min_connections: u32,
20 pub max_connections: u32,
21 pub scale_up_threshold: u32,
22 pub scale_down_threshold: u32,
23 pub scale_interval: Duration,
24 pub health_check_interval: Duration,
25 pub health_check_sql: String,
26}
27
28impl Default for ElasticConfig {
29 fn default() -> Self {
30 Self {
31 min_connections: 5,
32 max_connections: 50,
33 scale_up_threshold: 10,
34 scale_down_threshold: 20,
35 scale_interval: Duration::from_secs(1),
36 health_check_interval: Duration::from_secs(30),
37 health_check_sql: "SELECT 1".to_string(),
38 }
39 }
40}
41
42impl ElasticConfig {
43 pub fn validate(&self) -> Result<(), String> {
44 if self.min_connections < 1 || self.min_connections > 100 {
45 return Err("min_connections 必须在 [1,100]".to_string());
46 }
47 if self.max_connections < self.min_connections || self.max_connections > 1000 {
48 return Err("max_connections 必须在 [min,1000]".to_string());
49 }
50 if self.scale_interval < Duration::from_secs(1)
51 || self.scale_interval > Duration::from_secs(60)
52 {
53 return Err("scale_interval 必须在 [1s,60s]".to_string());
54 }
55 if self.health_check_interval < Duration::from_secs(10)
56 || self.health_check_interval > Duration::from_secs(300)
57 {
58 return Err("health_check_interval 必须在 [10s,300s]".to_string());
59 }
60 Ok(())
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
65pub enum ScaleReason {
66 HighLoad,
67 LowLoad,
68 HealthCheckFailure,
69}
70
71#[derive(Debug, Clone)]
72pub struct ScaleEvent {
73 pub from: u32,
74 pub to: u32,
75 pub reason: ScaleReason,
76 pub timestamp: Instant,
77 pub elapsed: Duration,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
85pub enum CircuitTier {
86 Connection,
87 Node,
88 Global,
89}
90
91pub struct TieredCircuitBreaker {
92 connection_level: Mutex<DefaultCircuitBreaker>,
93 node_level: Mutex<DefaultCircuitBreaker>,
94 global_level: Mutex<DefaultCircuitBreaker>,
95}
96
97impl TieredCircuitBreaker {
98 pub fn new(failure_threshold: usize, reset_timeout: Duration) -> Self {
99 Self {
100 connection_level: Mutex::new(DefaultCircuitBreaker::new(
101 failure_threshold,
102 reset_timeout,
103 )),
104 node_level: Mutex::new(DefaultCircuitBreaker::new(failure_threshold, reset_timeout)),
105 global_level: Mutex::new(DefaultCircuitBreaker::new(failure_threshold, reset_timeout)),
106 }
107 }
108
109 pub fn can_execute(&self, tier: CircuitTier) -> bool {
110 let cb = match tier {
111 CircuitTier::Connection => &self.connection_level,
112 CircuitTier::Node => &self.node_level,
113 CircuitTier::Global => &self.global_level,
114 };
115 cb.lock().unwrap().can_execute()
116 }
117
118 pub fn record_success(&self, tier: CircuitTier) {
119 let cb = match tier {
120 CircuitTier::Connection => &self.connection_level,
121 CircuitTier::Node => &self.node_level,
122 CircuitTier::Global => &self.global_level,
123 };
124 cb.lock().unwrap().record_success();
125 }
126
127 pub fn record_failure(&self, tier: CircuitTier) {
128 let cb = match tier {
129 CircuitTier::Connection => &self.connection_level,
130 CircuitTier::Node => &self.node_level,
131 CircuitTier::Global => &self.global_level,
132 };
133 cb.lock().unwrap().record_failure();
134 }
135
136 pub fn state(&self, tier: CircuitTier) -> CircuitState {
137 let cb = match tier {
138 CircuitTier::Connection => &self.connection_level,
139 CircuitTier::Node => &self.node_level,
140 CircuitTier::Global => &self.global_level,
141 };
142 cb.lock().unwrap().state()
143 }
144
145 pub fn can_execute_any(&self) -> bool {
146 self.can_execute(CircuitTier::Global)
147 && self.can_execute(CircuitTier::Node)
148 && self.can_execute(CircuitTier::Connection)
149 }
150}
151
152pub struct ConnectionHealthChecker {
157 failure_counts: Mutex<std::collections::HashMap<String, u32>>,
158 failure_threshold: u32,
159}
160
161impl ConnectionHealthChecker {
162 pub fn new(failure_threshold: u32) -> Self {
163 Self {
164 failure_counts: Mutex::new(std::collections::HashMap::new()),
165 failure_threshold,
166 }
167 }
168
169 pub fn record_failure(&self, conn_id: &str) -> bool {
170 let mut counts = self.failure_counts.lock().unwrap();
171 let count = counts.entry(conn_id.to_string()).or_insert(0);
172 *count += 1;
173 *count >= self.failure_threshold
174 }
175
176 pub fn record_success(&self, conn_id: &str) {
177 let mut counts = self.failure_counts.lock().unwrap();
178 counts.insert(conn_id.to_string(), 0);
179 }
180
181 pub fn should_evict(&self, conn_id: &str) -> bool {
182 let counts = self.failure_counts.lock().unwrap();
183 *counts.get(conn_id).unwrap_or(&0) >= self.failure_threshold
184 }
185
186 pub fn failure_count(&self, conn_id: &str) -> u32 {
187 let counts = self.failure_counts.lock().unwrap();
188 *counts.get(conn_id).unwrap_or(&0)
189 }
190}
191
192#[derive(Debug, Clone, Default)]
197pub struct PoolStats {
198 pub total_connections: u32,
199 pub idle_connections: u32,
200 pub waiting_requests: u32,
201}
202
203impl PoolStats {
204 pub fn active_connections(&self) -> u32 {
205 self.total_connections.saturating_sub(self.idle_connections)
206 }
207}
208
209pub struct PoolElasticController {
214 config: ElasticConfig,
215 circuit_breaker: TieredCircuitBreaker,
216 health_checker: ConnectionHealthChecker,
217 scale_events: Mutex<Vec<ScaleEvent>>,
218 current_size: Mutex<u32>,
219}
220
221impl PoolElasticController {
222 pub fn new(config: ElasticConfig) -> Self {
223 Self {
224 config,
225 circuit_breaker: TieredCircuitBreaker::new(5, Duration::from_secs(30)),
226 health_checker: ConnectionHealthChecker::new(3),
227 scale_events: Mutex::new(Vec::new()),
228 current_size: Mutex::new(5),
229 }
230 }
231
232 pub fn should_scale_up(stats: &PoolStats, config: &ElasticConfig) -> bool {
233 stats.waiting_requests > config.scale_up_threshold
234 }
235
236 pub fn should_scale_down(stats: &PoolStats, config: &ElasticConfig) -> bool {
237 stats.idle_connections > config.scale_down_threshold
238 }
239
240 pub fn evaluate_and_scale(&self, stats: &PoolStats) -> Option<ScaleEvent> {
241 let mut current = self.current_size.lock().unwrap();
242 let start = Instant::now();
243
244 if Self::should_scale_up(stats, &self.config) {
245 let target = (*current + stats.waiting_requests).min(self.config.max_connections);
246 if target > *current {
247 let event = ScaleEvent {
248 from: *current,
249 to: target,
250 reason: ScaleReason::HighLoad,
251 timestamp: Instant::now(),
252 elapsed: start.elapsed(),
253 };
254 *current = target;
255 self.scale_events.lock().unwrap().push(event.clone());
256 return Some(event);
257 }
258 }
259
260 if Self::should_scale_down(stats, &self.config) {
261 let target = (*current / 2).max(self.config.min_connections);
262 if target < *current {
263 let event = ScaleEvent {
264 from: *current,
265 to: target,
266 reason: ScaleReason::LowLoad,
267 timestamp: Instant::now(),
268 elapsed: start.elapsed(),
269 };
270 *current = target;
271 self.scale_events.lock().unwrap().push(event.clone());
272 return Some(event);
273 }
274 }
275
276 None
277 }
278
279 pub fn scale_events(&self) -> Vec<ScaleEvent> {
280 self.scale_events.lock().unwrap().clone()
281 }
282
283 pub fn current_size(&self) -> u32 {
284 *self.current_size.lock().unwrap()
285 }
286
287 pub fn circuit_breaker(&self) -> &TieredCircuitBreaker {
288 &self.circuit_breaker
289 }
290
291 pub fn health_checker(&self) -> &ConnectionHealthChecker {
292 &self.health_checker
293 }
294
295 pub fn config(&self) -> &ElasticConfig {
296 &self.config
297 }
298
299 pub fn prewarm(&self, target: u32) -> u32 {
300 let mut current = self.current_size.lock().unwrap();
301 let target = target
302 .min(self.config.max_connections)
303 .max(self.config.min_connections);
304 *current = target;
305 target
306 }
307}
308
309#[cfg(feature = "pool-io-reuse")]
320pub struct IoReuseChannel {
321 cache: crate::prepared_cache::PreparedStatementCache,
322}
323
324#[cfg(feature = "pool-io-reuse")]
325impl IoReuseChannel {
326 #[must_use]
330 pub fn new(max_size_per_conn: usize) -> Self {
331 Self {
332 cache: crate::prepared_cache::PreparedStatementCache::new(max_size_per_conn),
333 }
334 }
335
336 pub async fn execute_reuse(
349 &self,
350 conn_id: crate::prepared_cache::ConnId,
351 sql: &str,
352 params: &[crate::value::Value],
353 tables: Vec<String>,
354 prepare_fn: impl FnOnce() -> crate::prepared_cache::ExecuteFn,
355 ) -> Result<crate::pool::QueryRows, crate::error::DbError> {
356 use crate::prepared_cache::PreparedLookup;
357
358 match self.cache.get_or_prepare(conn_id, sql, params).await? {
359 PreparedLookup::Hit(rows) => Ok(rows),
360 PreparedLookup::Miss => {
361 let execute_fn = prepare_fn();
362 self.cache
363 .store_handle(conn_id, sql, tables, std::sync::Arc::clone(&execute_fn));
364 execute_fn(params).await
365 }
366 }
367 }
368
369 pub fn stats(&self) -> crate::prepared_cache::PreparedStatementCacheStatsSnapshot {
371 self.cache.stats()
372 }
373
374 pub fn invalidate_conn(&self, conn_id: crate::prepared_cache::ConnId) {
376 self.cache.invalidate_conn(conn_id);
377 }
378
379 pub fn invalidate_table(&self, table: &str) {
381 self.cache.invalidate_table(table);
382 }
383}
384
385#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
394 fn elastic_config_default_valid() {
395 let config = ElasticConfig::default();
396 assert!(config.validate().is_ok());
397 }
398
399 #[test]
400 fn elastic_config_invalid_min() {
401 let mut config = ElasticConfig::default();
402 config.min_connections = 0;
403 assert!(config.validate().is_err());
404 }
405
406 #[test]
407 fn elastic_config_invalid_max() {
408 let mut config = ElasticConfig::default();
409 config.max_connections = 3;
410 assert!(config.validate().is_err());
411 }
412
413 #[test]
414 fn tiered_circuit_independent() {
415 let cb = TieredCircuitBreaker::new(2, Duration::from_secs(60));
416 assert!(cb.can_execute(CircuitTier::Connection));
417 assert!(cb.can_execute(CircuitTier::Node));
418 assert!(cb.can_execute(CircuitTier::Global));
419
420 cb.record_failure(CircuitTier::Connection);
421 cb.record_failure(CircuitTier::Connection);
422 assert_eq!(cb.state(CircuitTier::Connection), CircuitState::Open);
423 assert_eq!(cb.state(CircuitTier::Node), CircuitState::Closed);
424 assert_eq!(cb.state(CircuitTier::Global), CircuitState::Closed);
425 }
426
427 #[test]
428 fn tiered_circuit_global_blocks_all() {
429 let cb = TieredCircuitBreaker::new(1, Duration::from_secs(60));
430 cb.record_failure(CircuitTier::Global);
431 assert_eq!(cb.state(CircuitTier::Global), CircuitState::Open);
432 assert!(!cb.can_execute_any());
433 }
434
435 #[test]
436 fn tiered_circuit_half_open_recovery() {
437 let cb = TieredCircuitBreaker::new(1, Duration::from_millis(10));
438 cb.record_failure(CircuitTier::Connection);
439 assert_eq!(cb.state(CircuitTier::Connection), CircuitState::Open);
440 std::thread::sleep(Duration::from_millis(20));
441 assert!(cb.can_execute(CircuitTier::Connection));
442 assert_eq!(cb.state(CircuitTier::Connection), CircuitState::HalfOpen);
443 cb.record_success(CircuitTier::Connection);
444 assert_eq!(cb.state(CircuitTier::Connection), CircuitState::Closed);
445 }
446
447 #[test]
448 fn health_check_single_failure_no_evict() {
449 let checker = ConnectionHealthChecker::new(3);
450 assert!(!checker.record_failure("conn-1"));
451 assert!(!checker.should_evict("conn-1"));
452 }
453
454 #[test]
455 fn health_check_three_failures_evict() {
456 let checker = ConnectionHealthChecker::new(3);
457 checker.record_failure("conn-1");
458 checker.record_failure("conn-1");
459 assert!(checker.record_failure("conn-1"));
460 assert!(checker.should_evict("conn-1"));
461 }
462
463 #[test]
464 fn health_check_success_resets() {
465 let checker = ConnectionHealthChecker::new(3);
466 checker.record_failure("conn-1");
467 checker.record_failure("conn-1");
468 checker.record_success("conn-1");
469 assert_eq!(checker.failure_count("conn-1"), 0);
470 assert!(!checker.should_evict("conn-1"));
471 }
472
473 #[test]
474 fn scale_up_on_high_load() {
475 let controller = PoolElasticController::new(ElasticConfig::default());
476 let stats = PoolStats {
477 total_connections: 10,
478 idle_connections: 0,
479 waiting_requests: 20,
480 };
481 let event = controller.evaluate_and_scale(&stats);
482 assert!(event.is_some());
483 let event = event.unwrap();
484 assert_eq!(event.reason, ScaleReason::HighLoad);
485 assert!(event.to > event.from);
486 }
487
488 #[test]
489 fn scale_down_on_low_load() {
490 let config = ElasticConfig {
491 min_connections: 2,
492 max_connections: 50,
493 scale_up_threshold: 10,
494 scale_down_threshold: 5,
495 scale_interval: Duration::from_secs(1),
496 health_check_interval: Duration::from_secs(30),
497 health_check_sql: "SELECT 1".to_string(),
498 };
499 let controller = PoolElasticController::new(config);
500 *controller.current_size.lock().unwrap() = 20;
501 let stats = PoolStats {
502 total_connections: 20,
503 idle_connections: 18,
504 waiting_requests: 0,
505 };
506 let event = controller.evaluate_and_scale(&stats);
507 assert!(event.is_some());
508 let event = event.unwrap();
509 assert_eq!(event.reason, ScaleReason::LowLoad);
510 assert!(event.to < event.from);
511 }
512
513 #[test]
514 fn scale_respects_max() {
515 let config = ElasticConfig {
516 min_connections: 1,
517 max_connections: 15,
518 scale_up_threshold: 5,
519 scale_down_threshold: 20,
520 scale_interval: Duration::from_secs(1),
521 health_check_interval: Duration::from_secs(30),
522 health_check_sql: "SELECT 1".to_string(),
523 };
524 let controller = PoolElasticController::new(config);
525 *controller.current_size.lock().unwrap() = 10;
526 let stats = PoolStats {
527 total_connections: 10,
528 idle_connections: 0,
529 waiting_requests: 100,
530 };
531 let event = controller.evaluate_and_scale(&stats).unwrap();
532 assert_eq!(event.to, 15, "不应超过 max_connections");
533 }
534
535 #[test]
536 fn scale_respects_min() {
537 let config = ElasticConfig {
538 min_connections: 5,
539 max_connections: 50,
540 scale_up_threshold: 10,
541 scale_down_threshold: 3,
542 scale_interval: Duration::from_secs(1),
543 health_check_interval: Duration::from_secs(30),
544 health_check_sql: "SELECT 1".to_string(),
545 };
546 let controller = PoolElasticController::new(config);
547 *controller.current_size.lock().unwrap() = 8;
548 let stats = PoolStats {
549 total_connections: 8,
550 idle_connections: 7,
551 waiting_requests: 0,
552 };
553 let event = controller.evaluate_and_scale(&stats).unwrap();
554 assert_eq!(event.to, 5, "不应低于 min_connections");
555 }
556
557 #[test]
558 fn no_scale_when_stable() {
559 let controller = PoolElasticController::new(ElasticConfig::default());
560 let stats = PoolStats {
561 total_connections: 10,
562 idle_connections: 5,
563 waiting_requests: 3,
564 };
565 let event = controller.evaluate_and_scale(&stats);
566 assert!(event.is_none());
567 }
568
569 #[test]
570 fn prewarm_to_min() {
571 let config = ElasticConfig {
572 min_connections: 5,
573 max_connections: 50,
574 scale_up_threshold: 10,
575 scale_down_threshold: 20,
576 scale_interval: Duration::from_secs(1),
577 health_check_interval: Duration::from_secs(30),
578 health_check_sql: "SELECT 1".to_string(),
579 };
580 let controller = PoolElasticController::new(config);
581 let warmed = controller.prewarm(5);
582 assert_eq!(warmed, 5);
583 assert_eq!(controller.current_size(), 5);
584 }
585
586 #[test]
587 fn prewarm_clamps_to_max() {
588 let config = ElasticConfig {
589 min_connections: 1,
590 max_connections: 10,
591 scale_up_threshold: 10,
592 scale_down_threshold: 20,
593 scale_interval: Duration::from_secs(1),
594 health_check_interval: Duration::from_secs(30),
595 health_check_sql: "SELECT 1".to_string(),
596 };
597 let controller = PoolElasticController::new(config);
598 let warmed = controller.prewarm(100);
599 assert_eq!(warmed, 10);
600 }
601
602 #[test]
603 fn scale_events_recorded() {
604 let controller = PoolElasticController::new(ElasticConfig::default());
605 let stats = PoolStats {
606 total_connections: 5,
607 idle_connections: 0,
608 waiting_requests: 20,
609 };
610 controller.evaluate_and_scale(&stats);
611 assert_eq!(controller.scale_events().len(), 1);
612 }
613
614 #[test]
615 fn wiring_public_api() {
616 let controller = PoolElasticController::new(ElasticConfig::default());
617 assert!(controller.config().validate().is_ok());
618 assert!(controller
619 .circuit_breaker()
620 .can_execute(CircuitTier::Global));
621 assert_eq!(controller.current_size(), 5);
622 }
623}