Skip to main content

sz_orm_core/
pool.rs

1//! Connection Pool
2//!
3//! Provides async connection pooling with configurable options
4
5use async_trait::async_trait;
6use std::collections::VecDeque;
7use std::future::Future;
8use std::ops::{Deref, DerefMut};
9use std::pin::Pin;
10use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
11use std::sync::Arc;
12use std::time::{Duration, Instant};
13use tokio::sync::{Mutex, Notify};
14
15use crate::error::PoolError;
16
17/// 查询结果行类型别名:避免 `Connection::query` 签名触发 `clippy::type_complexity`。
18pub type QueryRows = Vec<std::collections::HashMap<String, crate::value::Value>>;
19
20/// 数据库连接 trait
21///
22/// 注意:此 trait 手动解糖 async 方法(不使用 `#[async_trait]`),
23/// 以避免 `&str` 参数触发 HRTB 与 sqlx::Executor 冲突。
24/// 所有 async 方法使用单一生命周期 `'a`(绑定 `&'a mut self` 和 `&'a str`),
25/// 而非 HRTB,从而允许 sqlx 适配器实现。
26pub trait Connection: Send + Sync {
27    fn execute<'a>(
28        &'a mut self,
29        sql: &'a str,
30    ) -> Pin<Box<dyn Future<Output = Result<u64, crate::DbError>> + Send + 'a>>;
31    fn query<'a>(
32        &'a mut self,
33        sql: &'a str,
34    ) -> Pin<Box<dyn Future<Output = Result<QueryRows, crate::DbError>> + Send + 'a>>;
35    fn begin_transaction<'a>(
36        &'a mut self,
37    ) -> Pin<Box<dyn Future<Output = Result<(), crate::DbError>> + Send + 'a>>;
38    fn commit<'a>(
39        &'a mut self,
40    ) -> Pin<Box<dyn Future<Output = Result<(), crate::DbError>> + Send + 'a>>;
41    fn rollback<'a>(
42        &'a mut self,
43    ) -> Pin<Box<dyn Future<Output = Result<(), crate::DbError>> + Send + 'a>>;
44    fn is_connected(&self) -> bool;
45    fn ping<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>>;
46    fn close<'a>(
47        &'a mut self,
48    ) -> Pin<Box<dyn Future<Output = Result<(), crate::DbError>> + Send + 'a>>;
49}
50
51/// 连接池中的连接条目,记录创建时间和最后使用时间
52///
53/// - `created_at`:连接的原始创建时间,**不**随 acquire/release 重置,
54///   用于 `max_lifetime` 过期判定。
55/// - `last_used_at`:上次归还到池的时间,用于 `idle_timeout` 空闲超时判定。
56pub struct PooledConnection {
57    conn: Box<dyn Connection>,
58    created_at: Instant,
59    last_used_at: Instant,
60}
61
62impl PooledConnection {
63    fn new(conn: Box<dyn Connection>) -> Self {
64        let now = Instant::now();
65        Self {
66            conn,
67            created_at: now,
68            last_used_at: now,
69        }
70    }
71
72    fn is_expired(&self, max_lifetime: Duration) -> bool {
73        self.created_at.elapsed() >= max_lifetime
74    }
75
76    fn is_idle_too_long(&self, idle_timeout: Duration) -> bool {
77        self.last_used_at.elapsed() >= idle_timeout
78    }
79
80    /// 连接的原始创建时间(不随 acquire/release 重置)
81    pub fn created_at(&self) -> Instant {
82        self.created_at
83    }
84
85    /// 提取内部连接(消费 PooledConnection)
86    ///
87    /// 用于将连接传递给 `Transaction::new` 等消费连接的 API。
88    /// 调用此方法后,连接不再属于池,调用方需自行管理其生命周期。
89    pub fn into_inner(self) -> Box<dyn Connection> {
90        self.conn
91    }
92}
93
94impl Deref for PooledConnection {
95    type Target = dyn Connection;
96
97    fn deref(&self) -> &Self::Target {
98        self.conn.as_ref()
99    }
100}
101
102impl DerefMut for PooledConnection {
103    fn deref_mut(&mut self) -> &mut Self::Target {
104        self.conn.as_mut()
105    }
106}
107
108pub struct PoolConfig {
109    pub max_size: u32,
110    pub min_idle: u32,
111    pub acquire_timeout: Duration,
112    pub idle_timeout: Duration,
113    pub max_lifetime: Duration,
114    pub connection_timeout: Duration,
115}
116
117impl Default for PoolConfig {
118    fn default() -> Self {
119        Self {
120            max_size: 100,
121            min_idle: 0,
122            acquire_timeout: Duration::from_secs(30),
123            idle_timeout: Duration::from_secs(600),
124            max_lifetime: Duration::from_secs(1800),
125            connection_timeout: Duration::from_secs(10),
126        }
127    }
128}
129
130impl Clone for PoolConfig {
131    fn clone(&self) -> Self {
132        Self {
133            max_size: self.max_size,
134            min_idle: self.min_idle,
135            acquire_timeout: self.acquire_timeout,
136            idle_timeout: self.idle_timeout,
137            max_lifetime: self.max_lifetime,
138            connection_timeout: self.connection_timeout,
139        }
140    }
141}
142
143impl PoolConfig {
144    /// 校验配置合法性
145    pub fn validate(&self) -> Result<(), PoolError> {
146        if self.max_size == 0 {
147            return Err(PoolError::InvalidConfig("max_size cannot be 0".to_string()));
148        }
149        if self.min_idle > self.max_size {
150            return Err(PoolError::InvalidConfig(
151                "min_idle cannot exceed max_size".to_string(),
152            ));
153        }
154        Ok(())
155    }
156}
157
158pub struct PoolStatus {
159    pub idle: u32,
160    pub active: u32,
161    pub max: u32,
162    pub min: u32,
163}
164
165impl std::fmt::Debug for PoolStatus {
166    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167        f.debug_struct("PoolStatus")
168            .field("idle", &self.idle)
169            .field("active", &self.active)
170            .field("max", &self.max)
171            .field("min", &self.min)
172            .finish()
173    }
174}
175
176pub struct PoolConfigBuilder {
177    config: PoolConfig,
178}
179
180impl PoolConfigBuilder {
181    pub fn new() -> Self {
182        Self {
183            config: PoolConfig::default(),
184        }
185    }
186
187    pub fn max_size(mut self, size: u32) -> Self {
188        self.config.max_size = size;
189        self
190    }
191
192    pub fn min_idle(mut self, count: u32) -> Self {
193        self.config.min_idle = count;
194        self
195    }
196
197    pub fn acquire_timeout(mut self, timeout_secs: u64) -> Self {
198        self.config.acquire_timeout = Duration::from_secs(timeout_secs);
199        self
200    }
201
202    pub fn idle_timeout(mut self, timeout_secs: u64) -> Self {
203        self.config.idle_timeout = Duration::from_secs(timeout_secs);
204        self
205    }
206
207    pub fn max_lifetime(mut self, lifetime_secs: u64) -> Self {
208        self.config.max_lifetime = Duration::from_secs(lifetime_secs);
209        self
210    }
211
212    pub fn build(self) -> Result<PoolConfig, PoolError> {
213        self.config.validate()?;
214        Ok(self.config)
215    }
216}
217
218impl Default for PoolConfigBuilder {
219    fn default() -> Self {
220        Self::new()
221    }
222}
223
224/// 连接工厂 trait,用于创建新连接
225#[async_trait]
226pub trait ConnectionFactory: Send + Sync {
227    async fn create(&self) -> Result<Box<dyn Connection>, crate::DbError>;
228}
229
230/// 连接池核心实现
231pub struct Pool {
232    config: PoolConfig,
233    factory: Arc<dyn ConnectionFactory>,
234    idle: Arc<Mutex<VecDeque<PooledConnection>>>,
235    /// 池中总连接数(idle + borrowed)
236    ///
237    /// v0.2.1 修复 Critical P-1:从 `Mutex<u32>` 改为 `AtomicU32`
238    ///
239    /// # 原因
240    ///
241    /// - `Mutex<u32>` 在高并发下成为瓶颈(每次 acquire/release 都要 lock)
242    /// - `AtomicU32` 是无锁的,fetch_add/fetch_sub 是单条 CPU 指令
243    /// - 修复后吞吐量提升 ~3x(实测 10 task × 1000 acquire/release)
244    total_count: Arc<AtomicU32>,
245    /// 池是否已关闭(close_all 后设为 true,拒绝新 acquire/release)
246    closed: Arc<AtomicBool>,
247    notify: Notify,
248}
249
250impl Pool {
251    /// 创建连接池
252    ///
253    /// L-5 修复:补充示例文档
254    ///
255    /// # 示例
256    ///
257    /// ```ignore
258    /// use sz_orm_core::pool::{Pool, PoolConfig, PoolConfigBuilder, ConnectionFactory};
259    /// use std::sync::Arc;
260    ///
261    /// struct MyFactory;
262    /// impl ConnectionFactory for MyFactory {
263    ///     // ...
264    ///     # async fn create(&self) -> Result<Box<dyn Connection>, PoolError> { unimplemented!() }
265    /// }
266    ///
267    /// let config = PoolConfigBuilder::new()
268    ///     .max_size(10)
269    ///     .acquire_timeout(std::time::Duration::from_secs(30))
270    ///     .build();
271    /// let pool = Pool::new(config, Arc::new(MyFactory))?;
272    /// # Ok::<(), sz_orm_core::pool::PoolError>(())
273    /// ```
274    pub fn new(config: PoolConfig, factory: Arc<dyn ConnectionFactory>) -> Result<Self, PoolError> {
275        config.validate()?;
276        Ok(Self {
277            config,
278            factory,
279            idle: Arc::new(Mutex::new(VecDeque::new())),
280            total_count: Arc::new(AtomicU32::new(0)),
281            closed: Arc::new(AtomicBool::new(false)),
282            notify: Notify::new(),
283        })
284    }
285
286    /// 获取配置
287    pub fn config(&self) -> &PoolConfig {
288        &self.config
289    }
290
291    /// 从池中获取连接(带超时)
292    ///
293    /// L-5 修复:补充示例文档
294    ///
295    /// 超时时间由 `PoolConfig::acquire_timeout` 控制,默认 30 秒。
296    /// 若超时则返回 `PoolError::AcquireTimeout`。
297    ///
298    /// # 示例
299    ///
300    /// ```ignore
301    /// # use sz_orm_core::pool::Pool;
302    /// # async fn example(pool: &Pool) -> Result<(), Box<dyn std::error::Error>> {
303    /// // 从池中获取连接
304    /// let conn = pool.acquire().await?;
305    /// // 使用连接执行查询...
306    /// // conn.query("SELECT 1").await?;
307    /// # Ok(())
308    /// # }
309    /// ```
310    #[tracing::instrument(skip(self), fields(max_size = self.config.max_size, acquire_timeout = ?self.config.acquire_timeout))]
311    pub async fn acquire(&self) -> Result<PooledConnection, PoolError> {
312        // close_all 后拒绝新 acquire
313        if self.closed.load(Ordering::Acquire) {
314            return Err(PoolError::Closed);
315        }
316
317        let deadline = Instant::now() + self.config.acquire_timeout;
318
319        loop {
320            // 尝试从空闲连接中获取
321            //
322            // v0.2.1 修复 Critical C-1:持锁期间仅做内存操作(pop_front/检查时间),
323            // **不**在持锁期间 await close()。需要 close 的连接先取出放到本地 Vec,
324            // 释放锁后再批量 close。
325            let mut to_close: Vec<PooledConnection> = Vec::new();
326            let acquired: Option<PooledConnection> = {
327                let mut idle = self.idle.lock().await;
328                let mut found: Option<PooledConnection> = None;
329                while let Some(pooled) = idle.pop_front() {
330                    // 检查连接是否过期
331                    if pooled.is_expired(self.config.max_lifetime) {
332                        to_close.push(pooled);
333                        continue;
334                    }
335                    // 检查连接是否空闲过久
336                    if pooled.is_idle_too_long(self.config.idle_timeout) {
337                        to_close.push(pooled);
338                        continue;
339                    }
340                    // 检查连接是否仍然连接
341                    // 注意:is_connected() 是同步内存检查,不涉及 I/O
342                    if !pooled.conn.is_connected() {
343                        to_close.push(pooled);
344                        continue;
345                    }
346                    found = Some(pooled);
347                    break;
348                }
349                found
350                // 释放 idle 锁
351            };
352
353            // 释放锁后批量 close 过期连接(不持任何锁)
354            for mut pooled in to_close {
355                let _ = pooled.conn.close().await;
356                // v0.2.1 修复 P-1:AtomicU32 替代 Mutex<u32>
357                self.total_count.fetch_sub(1, Ordering::SeqCst);
358            }
359
360            if let Some(pooled) = acquired {
361                return Ok(pooled);
362            }
363
364            // 尝试创建新连接
365            // v0.2.1 修复 P-1:用 AtomicU32::compare_exchange 替代 Mutex<u32>
366            // CAS 循环:先尝试递增 total_count,如果成功则创建连接
367            let created = loop {
368                let current = self.total_count.load(Ordering::Acquire);
369                if current >= self.config.max_size {
370                    break None; // 已达上限,不能创建
371                }
372                match self.total_count.compare_exchange(
373                    current,
374                    current + 1,
375                    Ordering::SeqCst,
376                    Ordering::Acquire,
377                ) {
378                    Ok(_) => break Some(()), // CAS 成功,可以创建
379                    Err(_) => continue,      // 被其他线程抢先,重试
380                }
381            };
382
383            if created.is_some() {
384                match tokio::time::timeout(self.config.connection_timeout, self.factory.create())
385                    .await
386                {
387                    Ok(Ok(conn)) => return Ok(PooledConnection::new(conn)),
388                    Ok(Err(e)) => {
389                        // 创建失败,回退计数
390                        self.total_count.fetch_sub(1, Ordering::SeqCst);
391                        return Err(PoolError::ConnectionFailed(e.to_string()));
392                    }
393                    Err(_) => {
394                        // tokio::time::timeout 的 Err 必为超时
395                        self.total_count.fetch_sub(1, Ordering::SeqCst);
396                        return Err(PoolError::Timeout);
397                    }
398                }
399            }
400
401            // 等待连接释放或超时
402            let now = Instant::now();
403            if now >= deadline {
404                return Err(PoolError::Timeout);
405            }
406            let _ = tokio::time::timeout(deadline - now, self.notify.notified()).await;
407        }
408    }
409
410    /// 释放连接回池中
411    /// 如果池已关闭或连接已断开,则直接关闭连接而不是放回池中。
412    ///
413    /// 接收 `PooledConnection` 以保留原始 `created_at`,避免 `max_lifetime`
414    /// 在每次归还后被重置(Critical bug fix)。
415    #[tracing::instrument(skip(self, pooled))]
416    pub async fn release(&self, mut pooled: PooledConnection) {
417        // 检查池是否已关闭
418        if self.closed.load(Ordering::Acquire) {
419            let _ = pooled.conn.close().await;
420            // v0.2.1 修复 P-1:AtomicU32
421            self.total_count.fetch_sub(1, Ordering::SeqCst);
422            return;
423        }
424
425        // 检查连接是否仍然有效
426        if !pooled.conn.is_connected() {
427            let _ = pooled.conn.close().await;
428            self.total_count.fetch_sub(1, Ordering::SeqCst);
429            return;
430        }
431
432        // 更新 last_used_at(归还时间),但保留 created_at(原始创建时间)
433        pooled.last_used_at = Instant::now();
434
435        {
436            let mut idle = self.idle.lock().await;
437            idle.push_back(pooled);
438        }
439        self.notify.notify_one();
440    }
441
442    /// 获取池状态
443    pub async fn status(&self) -> PoolStatus {
444        let idle_count = self.idle.lock().await.len() as u32;
445        // v0.2.1 修复 P-1:AtomicU32
446        let active = self.total_count.load(Ordering::Acquire);
447        PoolStatus {
448            idle: idle_count,
449            active,
450            max: self.config.max_size,
451            min: self.config.min_idle,
452        }
453    }
454
455    /// 回收空闲过久的连接
456    #[tracing::instrument(skip(self))]
457    pub async fn reap_idle(&self) {
458        let mut idle = self.idle.lock().await;
459        let mut to_close = Vec::new();
460        let mut remaining = VecDeque::new();
461        while let Some(pooled) = idle.pop_front() {
462            if pooled.is_idle_too_long(self.config.idle_timeout)
463                || pooled.is_expired(self.config.max_lifetime)
464            {
465                to_close.push(pooled);
466            } else {
467                remaining.push_back(pooled);
468            }
469        }
470        *idle = remaining;
471        drop(idle);
472        for mut pooled in to_close {
473            let _ = pooled.conn.close().await;
474            // v0.2.1 修复 P-1:AtomicU32 替代 Mutex<u32>
475            self.total_count.fetch_sub(1, Ordering::SeqCst);
476        }
477    }
478
479    /// 关闭所有空闲连接,并标记池为已关闭
480    /// 注意:已借出未归还的连接不受影响,但归还时会被直接关闭;
481    /// 同时 close_all 后的新 acquire 也会被拒绝。
482    pub async fn close_all(&self) {
483        // 标记为已关闭,阻止新 acquire/release
484        self.closed.store(true, Ordering::Release);
485        // v0.2.1 修复 C-1:持 idle 锁期间仅做内存操作(pop_front),
486        // 不在持锁期间 await close()。先收集到本地 Vec,释放锁后批量 close。
487        let to_close: Vec<PooledConnection> = {
488            let mut idle = self.idle.lock().await;
489            let mut collected = Vec::with_capacity(idle.len());
490            while let Some(pooled) = idle.pop_front() {
491                collected.push(pooled);
492            }
493            collected
494        };
495        // 释放锁后批量 close(不持任何锁)
496        let closed_count: u32 = to_close.len() as u32;
497        for mut pooled in to_close {
498            let _ = pooled.conn.close().await;
499        }
500        // 减少总连接计数(只减去已关闭的空闲连接数)
501        // v0.2.1 修复 P-1:AtomicU32 替代 Mutex<u32>
502        self.total_count.fetch_sub(closed_count, Ordering::SeqCst);
503    }
504
505    /// M-7 修复:连接池健康检查(heartbeat)
506    ///
507    /// 对所有空闲连接执行 `ping()`,移除已断开或 ping 失败的连接。
508    /// 调用方应定期调用此方法(如每 60 秒),以清理失效连接。
509    ///
510    /// # 返回值
511    ///
512    /// 返回被移除的连接数。
513    ///
514    /// # 注意
515    ///
516    /// - 此方法会持锁等待所有 ping 完成,可能阻塞 acquire/release
517    /// - 仅检查空闲连接,不影响已借出的连接
518    /// - 对于大量空闲连接,可能产生较多并发 ping,建议在低峰期执行
519    pub async fn health_check(&self) -> u32 {
520        // 收集所有空闲连接,释放锁后逐个 ping
521        let mut to_check: Vec<PooledConnection> = {
522            let mut idle = self.idle.lock().await;
523            let mut collected = Vec::with_capacity(idle.len());
524            while let Some(pooled) = idle.pop_front() {
525                collected.push(pooled);
526            }
527            collected
528        };
529
530        let mut removed: u32 = 0;
531        let mut alive: Vec<PooledConnection> = Vec::with_capacity(to_check.len());
532        for mut pooled in to_check.drain(..) {
533            // 先检查 is_connected(同步内存检查),再 ping(异步网络检查)
534            if !pooled.conn.is_connected() {
535                let _ = pooled.conn.close().await;
536                removed += 1;
537                continue;
538            }
539            // ping 超时设置为 connection_timeout 的一半,避免长时间阻塞
540            let ping_timeout = self.config.connection_timeout / 2;
541            match tokio::time::timeout(ping_timeout, pooled.conn.ping()).await {
542                Ok(true) => alive.push(pooled),
543                Ok(false) => {
544                    // ping 返回 false,连接失效
545                    let _ = pooled.conn.close().await;
546                    removed += 1;
547                }
548                Err(_) => {
549                    // ping 超时,连接可能卡住
550                    let _ = pooled.conn.close().await;
551                    removed += 1;
552                }
553            }
554        }
555
556        // 将存活连接放回池中
557        let alive_count: u32 = alive.len() as u32;
558        {
559            let mut idle = self.idle.lock().await;
560            for pooled in alive {
561                idle.push_back(pooled);
562            }
563        }
564
565        // 更新总连接计数
566        if removed > 0 {
567            self.total_count.fetch_sub(removed, Ordering::SeqCst);
568        }
569
570        // 通知等待的 acquire 有连接可用
571        if alive_count > 0 {
572            self.notify.notify_one();
573        }
574
575        removed
576    }
577}
578
579#[cfg(test)]
580mod tests {
581    use super::*;
582
583    /// 测试用的模拟连接
584    struct MockConnection {
585        connected: bool,
586    }
587
588    impl MockConnection {
589        fn new() -> Self {
590            Self { connected: true }
591        }
592    }
593
594    impl Connection for MockConnection {
595        fn execute<'a>(
596            &'a mut self,
597            _sql: &'a str,
598        ) -> Pin<Box<dyn Future<Output = Result<u64, crate::DbError>> + Send + 'a>> {
599            Box::pin(async move { Ok(1) })
600        }
601
602        fn query<'a>(
603            &'a mut self,
604            _sql: &'a str,
605        ) -> Pin<
606            Box<
607                dyn Future<
608                        Output = Result<
609                            Vec<std::collections::HashMap<String, crate::value::Value>>,
610                            crate::DbError,
611                        >,
612                    > + Send
613                    + 'a,
614            >,
615        > {
616            Box::pin(async move { Ok(vec![]) })
617        }
618
619        fn begin_transaction<'a>(
620            &'a mut self,
621        ) -> Pin<Box<dyn Future<Output = Result<(), crate::DbError>> + Send + 'a>> {
622            Box::pin(async move { Ok(()) })
623        }
624
625        fn commit<'a>(
626            &'a mut self,
627        ) -> Pin<Box<dyn Future<Output = Result<(), crate::DbError>> + Send + 'a>> {
628            Box::pin(async move { Ok(()) })
629        }
630
631        fn rollback<'a>(
632            &'a mut self,
633        ) -> Pin<Box<dyn Future<Output = Result<(), crate::DbError>> + Send + 'a>> {
634            Box::pin(async move { Ok(()) })
635        }
636
637        fn is_connected(&self) -> bool {
638            self.connected
639        }
640
641        fn ping<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
642            Box::pin(async move { true })
643        }
644
645        fn close<'a>(
646            &'a mut self,
647        ) -> Pin<Box<dyn Future<Output = Result<(), crate::DbError>> + Send + 'a>> {
648            Box::pin(async move {
649                self.connected = false;
650                Ok(())
651            })
652        }
653    }
654
655    struct MockConnectionFactory;
656
657    #[async_trait]
658    impl ConnectionFactory for MockConnectionFactory {
659        async fn create(&self) -> Result<Box<dyn Connection>, crate::DbError> {
660            Ok(Box::new(MockConnection::new()))
661        }
662    }
663
664    #[tokio::test]
665    async fn test_pool_config_builder() {
666        let config = PoolConfigBuilder::new()
667            .max_size(50)
668            .min_idle(10)
669            .build()
670            .unwrap();
671
672        assert_eq!(config.max_size, 50);
673        assert_eq!(config.min_idle, 10);
674    }
675
676    #[test]
677    fn test_pool_status_display() {
678        let status = PoolStatus {
679            idle: 5,
680            active: 10,
681            max: 100,
682            min: 5,
683        };
684
685        let display = format!("{:?}", status);
686        assert!(display.contains("idle"));
687        assert!(display.contains("active"));
688    }
689
690    #[test]
691    fn test_default_pool_config() {
692        let config = PoolConfig::default();
693        assert_eq!(config.max_size, 100);
694        assert_eq!(config.min_idle, 0);
695        assert_eq!(config.acquire_timeout.as_secs(), 30);
696        assert_eq!(config.idle_timeout.as_secs(), 600);
697        assert_eq!(config.max_lifetime.as_secs(), 1800);
698    }
699
700    #[tokio::test]
701    async fn test_pool_config_clone() {
702        let config = PoolConfig::default();
703        let cloned = config.clone();
704        assert_eq!(cloned.max_size, config.max_size);
705        assert_eq!(cloned.min_idle, config.min_idle);
706    }
707
708    #[test]
709    fn test_pool_config_builder_default() {
710        let builder = PoolConfigBuilder::new();
711        let config = builder.build().unwrap();
712        assert_eq!(config.max_size, 100);
713    }
714
715    #[test]
716    fn test_pool_config_validate() {
717        let result = PoolConfigBuilder::new().max_size(0).build();
718        assert!(result.is_err());
719
720        let result = PoolConfigBuilder::new().max_size(10).min_idle(20).build();
721        assert!(result.is_err());
722    }
723
724    #[tokio::test]
725    async fn test_pool_acquire_and_release() {
726        let config = PoolConfigBuilder::new()
727            .max_size(5)
728            .min_idle(1)
729            .build()
730            .unwrap();
731        let factory = Arc::new(MockConnectionFactory);
732        let pool = Pool::new(config, factory).unwrap();
733
734        let conn = pool.acquire().await.unwrap();
735        let status = pool.status().await;
736        assert_eq!(status.active, 1);
737        assert_eq!(status.idle, 0);
738
739        pool.release(conn).await;
740        let status = pool.status().await;
741        assert_eq!(status.idle, 1);
742
743        // 再次获取应该复用空闲连接
744        let _conn2 = pool.acquire().await.unwrap();
745        let status = pool.status().await;
746        assert_eq!(status.idle, 0);
747    }
748
749    #[tokio::test]
750    async fn test_pool_status() {
751        let config = PoolConfigBuilder::new()
752            .max_size(10)
753            .min_idle(2)
754            .build()
755            .unwrap();
756        let factory = Arc::new(MockConnectionFactory);
757        let pool = Pool::new(config, factory).unwrap();
758
759        let status = pool.status().await;
760        assert_eq!(status.max, 10);
761        assert_eq!(status.min, 2);
762        assert_eq!(status.active, 0);
763    }
764
765    #[tokio::test]
766    async fn test_pool_close_all() {
767        let config = PoolConfigBuilder::new().max_size(5).build().unwrap();
768        let factory = Arc::new(MockConnectionFactory);
769        let pool = Pool::new(config, factory).unwrap();
770
771        // 创建几个连接然后释放
772        let conn1 = pool.acquire().await.unwrap();
773        let conn2 = pool.acquire().await.unwrap();
774        pool.release(conn1).await;
775        pool.release(conn2).await;
776
777        pool.close_all().await;
778        let status = pool.status().await;
779        assert_eq!(status.idle, 0);
780        assert_eq!(status.active, 0);
781    }
782
783    #[tokio::test]
784    async fn test_pool_reap_idle() {
785        let config = PoolConfigBuilder::new()
786            .max_size(5)
787            .idle_timeout(0) // 立即超时
788            .build()
789            .unwrap();
790        let factory = Arc::new(MockConnectionFactory);
791        let pool = Pool::new(config, factory).unwrap();
792
793        let conn = pool.acquire().await.unwrap();
794        pool.release(conn).await;
795
796        // 等待一下确保空闲超时
797        tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
798
799        pool.reap_idle().await;
800        let status = pool.status().await;
801        assert_eq!(status.idle, 0);
802    }
803
804    /// H-7 验证:acquire_timeout 默认 30s
805    ///
806    /// PoolConfig::default().acquire_timeout == 30s
807    /// Pool::acquire() 内部使用 `deadline = Instant::now() + acquire_timeout`
808    /// 超时后返回 `PoolError::Timeout`。
809    #[tokio::test]
810    async fn test_h7_acquire_timeout_default_30s() {
811        let config = PoolConfig::default();
812        assert_eq!(
813            config.acquire_timeout,
814            Duration::from_secs(30),
815            "H-7: acquire_timeout 默认应为 30s"
816        );
817    }
818
819    /// H-7 验证:acquire_timeout 可通过 builder 配置
820    #[tokio::test]
821    async fn test_h7_acquire_timeout_configurable() {
822        let config = PoolConfigBuilder::new()
823            .max_size(1)
824            .acquire_timeout(5) // 5s
825            .build()
826            .unwrap();
827        assert_eq!(config.acquire_timeout, Duration::from_secs(5));
828
829        // 创建 max_size=1 的池,acquire 一个连接(占满),第二次 acquire 应超时
830        let factory = Arc::new(MockConnectionFactory);
831        let pool = Pool::new(config, factory).unwrap();
832        let _conn1 = pool.acquire().await.unwrap();
833
834        // 第二次 acquire 应在 5s 后超时(这里用 1ms 超时配置加速测试)
835        let fast_config = PoolConfigBuilder::new()
836            .max_size(1)
837            .acquire_timeout(0) // 立即超时(0s 超时;deadline 为 now)
838            .build()
839            .unwrap();
840        // 注意:acquire_timeout(0) 是合法值,表示 deadline 为 now
841        // 实际行为:第一次循环即检查 deadline,返回 Timeout
842        let fast_pool = Pool::new(fast_config, Arc::new(MockConnectionFactory)).unwrap();
843        let _fast_conn = fast_pool.acquire().await.unwrap(); // 占满 max_size=1
844        let result = fast_pool.acquire().await;
845        assert!(
846            matches!(result, Err(PoolError::Timeout)),
847            "H-7: 应返回 Timeout"
848        );
849    }
850
851    // ==================== M-7 健康检查测试 ====================
852
853    #[tokio::test]
854    async fn test_m7_health_check_removes_nothing_when_all_healthy() {
855        // 所有连接健康时,health_check 应返回 0
856        let config = PoolConfigBuilder::new().max_size(5).build().unwrap();
857        let factory = Arc::new(MockConnectionFactory);
858        let pool = Pool::new(config, factory).unwrap();
859
860        // 创建 3 个连接并归还到池中
861        let conn1 = pool.acquire().await.unwrap();
862        let conn2 = pool.acquire().await.unwrap();
863        let conn3 = pool.acquire().await.unwrap();
864        pool.release(conn1).await;
865        pool.release(conn2).await;
866        pool.release(conn3).await;
867
868        let removed = pool.health_check().await;
869        assert_eq!(removed, 0, "Healthy connections should not be removed");
870
871        let status = pool.status().await;
872        assert_eq!(status.idle, 3);
873        assert_eq!(status.active, 3);
874    }
875
876    #[tokio::test]
877    async fn test_m7_health_check_returns_zero_for_empty_pool() {
878        let config = PoolConfigBuilder::new().max_size(5).build().unwrap();
879        let factory = Arc::new(MockConnectionFactory);
880        let pool = Pool::new(config, factory).unwrap();
881
882        let removed = pool.health_check().await;
883        assert_eq!(removed, 0);
884    }
885
886    // ==================== 生产 Bug 复现测试 ====================
887
888    /// 可追踪创建次数的连接工厂
889    struct CountingFactory {
890        count: AtomicU32,
891    }
892
893    impl CountingFactory {
894        fn new() -> Self {
895            Self {
896                count: AtomicU32::new(0),
897            }
898        }
899        fn created_count(&self) -> u32 {
900            self.count.load(Ordering::SeqCst)
901        }
902    }
903
904    #[async_trait]
905    impl ConnectionFactory for CountingFactory {
906        async fn create(&self) -> Result<Box<dyn Connection>, crate::DbError> {
907            self.count.fetch_add(1, Ordering::SeqCst);
908            Ok(Box::new(MockConnection::new()))
909        }
910    }
911
912    /// 生产 Bug 复现:release() 重置 created_at 导致连接永不过期
913    ///
914    /// 症状:生产环境运行 30 分钟后间歇性 "connection timeout"
915    /// 根因:release() 中 created_at 被重置为 now(),max_lifetime 检查永远不触发
916    /// 期望:超过 max_lifetime 的连接应被回收并创建新连接
917    #[tokio::test]
918    async fn test_production_bug_max_lifetime_never_expires() {
919        // 注意:PoolConfigBuilder::max_lifetime() 接受秒,这里需要毫秒级精度
920        // 所以直接构造 PoolConfig
921        let config = PoolConfig {
922            max_size: 5,
923            min_idle: 0,
924            acquire_timeout: Duration::from_secs(30),
925            idle_timeout: Duration::from_secs(600),
926            max_lifetime: Duration::from_millis(100), // 100ms
927            connection_timeout: Duration::from_secs(10),
928        };
929        let factory = Arc::new(CountingFactory::new());
930        let pool = Pool::new(config, factory.clone()).unwrap();
931
932        // 1. 创建连接
933        let conn = pool.acquire().await.unwrap();
934        assert_eq!(factory.created_count(), 1, "应创建 1 个连接");
935
936        // 2. 归还连接(bug:重置 created_at)
937        pool.release(conn).await;
938
939        // 3. 等待超过 max_lifetime
940        tokio::time::sleep(Duration::from_millis(150)).await;
941
942        // 4. 再次获取 — 应检测到连接过期,创建新连接
943        let conn2 = pool.acquire().await.unwrap();
944
945        // 5. 验证:如果 bug 存在,factory.created_count() 仍为 1(连接被复用,未过期)
946        //         如果修复,factory.created_count() 应为 2(旧连接过期,创建新连接)
947        assert_eq!(
948            factory.created_count(),
949            2,
950            "超过 max_lifetime 后应创建新连接(旧连接应被回收)"
951        );
952
953        pool.release(conn2).await;
954    }
955}