├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── performance.rs ├── example ├── Cargo.toml └── src │ └── main.rs ├── src ├── defer.rs ├── duration.rs ├── guard.rs ├── lib.rs ├── plugin │ ├── check_duration_manager.rs │ └── mod.rs ├── pool.rs └── state.rs └── tests ├── duration_manager_test.rs └── pool_test.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # idea 6 | /.idea/ 7 | /.idea 8 | .idea/ 9 | '.idea' 10 | # vscode 11 | /.vscode/ 12 | .vscode/ 13 | /.vscode 14 | '.vscode' 15 | /.github 16 | 17 | 18 | 19 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 20 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 21 | Cargo.lock 22 | 23 | # These are backup files generated by rustfmt 24 | **/*.rs.bk 25 | 26 | 27 | #log 28 | *.log 29 | 30 | #mac 31 | *.DS_Store 32 | 33 | 34 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | workspace = { members = ["example"] } 2 | [package] 3 | name = "fast_pool" 4 | version = "0.3.7" 5 | edition = "2021" 6 | description = "The Fast Pool based on channel" 7 | readme = "README.md" 8 | authors = ["ce "] 9 | license = "Apache-2.0" 10 | categories = ["caching"] 11 | keywords = ["database", "orm", "pool"] 12 | repository = "https://github.com/rbatis/fast_pool" 13 | homepage = "https://github.com/rbatis/fast_pool" 14 | 15 | [dependencies] 16 | async-trait = "0.1" 17 | futures-core = { version = "0.3" } 18 | tokio = { version = "1", features = ["time", "rt-multi-thread", "macros"] } 19 | flume = { version = "0.11.0", default-features = false, features = ["async"] } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fast_pool 2 | 3 | [![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/) 4 | [![GitHub release](https://img.shields.io/github/v/release/rbatis/fast_pool)](https://github.com/rbatis/fast_pool/releases) 5 | 6 | 7 | 8 | a fast async pool based on channel 9 | * support `get()`,`get_timeout()`,`state()` methods 10 | * support atomic max_open(Resize freely) 11 | * based on [flume](https://crates.io/crates/flume) 12 | 13 | ### why fast_pool? 14 | 15 | * fast get() method performance 16 | ```log 17 | //windows: 18 | //---- bench_pool stdout ---- 19 | //Time: 14.0994ms ,each:140 ns/op 20 | //QPS: 7086167 QPS/s 21 | ``` 22 | * Implement using only channels 23 | * no additional threads 24 | * Support asynchronous/tokio 25 | 26 | 27 | ### how to use this? 28 | 29 | * step 1 add toml 30 | ```toml 31 | fast_pool="0.3" 32 | tokio = {version = "1",features = ["time","rt-multi-thread","macros"]} 33 | ``` 34 | * step 2 impl trait 35 | ```rust 36 | use std::ops::{DerefMut}; 37 | use std::time::Duration; 38 | use async_trait::async_trait; 39 | use fast_pool::{Manager, Pool}; 40 | 41 | #[derive(Debug)] 42 | pub struct TestManager {} 43 | 44 | impl Manager for TestManager { 45 | type Connection = String; 46 | type Error = String; 47 | 48 | async fn connect(&self) -> Result { 49 | Ok("conn".to_string()) 50 | } 51 | 52 | async fn check(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { 53 | //check should use conn.ping() 54 | if conn == "error" { 55 | return Err(Self::Error::from("error".to_string())); 56 | } 57 | Ok(()) 58 | } 59 | } 60 | 61 | #[tokio::main] 62 | async fn main() { 63 | let p = Pool::new(TestManager {}); 64 | println!("status = {}",p.state()); 65 | p.set_max_open(10); 66 | println!("status = {}",p.state()); 67 | 68 | let mut conn = p.get().await.unwrap(); 69 | println!("conn = {}",conn.deref_mut()); 70 | let mut conn = p.get_timeout(Some(Duration::from_secs(1))).await.unwrap(); 71 | println!("conn = {}",conn.deref_mut()); 72 | } 73 | ``` 74 | -------------------------------------------------------------------------------- /benches/performance.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_mut)] 2 | #![allow(unused_imports)] 3 | #![allow(unreachable_patterns)] 4 | #![allow(unused_variables)] 5 | #![allow(unused_assignments)] 6 | #![allow(unused_must_use)] 7 | #![allow(dead_code)] 8 | #![feature(test)] 9 | extern crate test; 10 | 11 | use futures_core::future::BoxFuture; 12 | use std::any::Any; 13 | use std::future::Future; 14 | use test::Bencher; 15 | 16 | pub trait QPS { 17 | fn qps(&self, total: u64); 18 | fn time(&self, total: u64); 19 | fn cost(&self); 20 | } 21 | 22 | impl QPS for std::time::Instant { 23 | fn qps(&self, total: u64) { 24 | let time = self.elapsed(); 25 | println!( 26 | "QPS: {} QPS/s", 27 | (total as u128 * 1000000000 as u128 / time.as_nanos() as u128) 28 | ); 29 | } 30 | 31 | fn time(&self, total: u64) { 32 | let time = self.elapsed(); 33 | println!( 34 | "Time: {:?} ,each:{} ns/op", 35 | &time, 36 | time.as_nanos() / (total as u128) 37 | ); 38 | } 39 | 40 | fn cost(&self) { 41 | let time = self.elapsed(); 42 | println!("cost:{:?}", time); 43 | } 44 | } 45 | 46 | #[macro_export] 47 | macro_rules! rbench { 48 | ($total:expr,$body:block) => {{ 49 | let now = std::time::Instant::now(); 50 | for _ in 0..$total { 51 | $body; 52 | } 53 | now.time($total); 54 | now.qps($total); 55 | }}; 56 | } 57 | 58 | pub fn block_on(task: T) -> R 59 | where 60 | T: Future + Send + 'static, 61 | T::Output: Send + 'static, 62 | { 63 | tokio::task::block_in_place(|| { 64 | tokio::runtime::Builder::new_multi_thread() 65 | .enable_all() 66 | .build() 67 | .expect("tokio block_on fail") 68 | .block_on(task) 69 | }) 70 | } 71 | 72 | //cargo test --release --package fast_pool --bench performance bench_pool --no-fail-fast -- --exact -Z unstable-options --show-output 73 | //windows: 74 | //---- bench_pool stdout ---- 75 | //Time: 14.0994ms ,each:140 ns/op 76 | //QPS: 7086167 QPS/s 77 | //macos: 78 | //---- bench_pool stdout ---- 79 | //Time: 6.373708ms ,each:63 ns/op 80 | //QPS: 15683710 QPS/s 81 | #[test] 82 | fn bench_pool() { 83 | use async_trait::async_trait; 84 | use fast_pool::{Manager, Pool}; 85 | 86 | pub struct TestManager {} 87 | 88 | impl Manager for TestManager { 89 | type Connection = i32; 90 | type Error = String; 91 | 92 | async fn connect(&self) -> Result { 93 | Ok(0) 94 | } 95 | 96 | async fn check(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { 97 | Ok(()) 98 | } 99 | } 100 | let f = async { 101 | let p = Pool::new(TestManager {}); 102 | rbench!(100000, { 103 | let v = p.get().await.unwrap(); 104 | }); 105 | }; 106 | block_on(f); 107 | } 108 | -------------------------------------------------------------------------------- /example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | fast_pool = {path = "../"} 10 | tokio = { version = "1", features = ["time", "rt-multi-thread", "macros"] } -------------------------------------------------------------------------------- /example/src/main.rs: -------------------------------------------------------------------------------- 1 | use fast_pool::{Manager, Pool}; 2 | use std::ops::DerefMut; 3 | use std::time::Duration; 4 | 5 | #[derive(Debug)] 6 | pub struct TestManager {} 7 | 8 | impl Manager for TestManager { 9 | type Connection = String; 10 | type Error = String; 11 | 12 | async fn connect(&self) -> Result { 13 | Ok("conn".to_string()) 14 | } 15 | 16 | async fn check(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { 17 | //check should use conn.ping() 18 | if conn == "error" { 19 | return Err(Self::Error::from("error".to_string())); 20 | } 21 | Ok(()) 22 | } 23 | } 24 | 25 | #[tokio::main] 26 | async fn main() { 27 | let p = Pool::new(TestManager {}); 28 | println!("state = {}", p.state()); 29 | p.set_max_open(10); 30 | println!("state = {}", p.state()); 31 | 32 | let mut conn = p.get().await.unwrap(); 33 | println!("conn = {}", conn.deref_mut()); 34 | let mut conn = p.get_timeout(Some(Duration::from_secs(1))).await.unwrap(); 35 | println!("conn = {}", conn.deref_mut()); 36 | } 37 | -------------------------------------------------------------------------------- /src/defer.rs: -------------------------------------------------------------------------------- 1 | pub struct Guard(pub Option); 2 | 3 | impl Drop for Guard { 4 | fn drop(&mut self) { 5 | if let Some(mut f) = (self.0).take() { 6 | f() 7 | } 8 | } 9 | } 10 | 11 | macro_rules! defer { 12 | ($func:block) => { 13 | let _guard = $crate::defer::Guard(Some( ||$func)); 14 | }; 15 | ($func:expr) => { 16 | let _guard = $crate::defer::Guard(Some($func)); 17 | }; 18 | { $($func:expr$(;)?)+ } => { 19 | let _guard = $crate::defer::Guard(Some( ||{$($func;)+})); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/duration.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicUsize, Ordering}; 2 | use std::time::Duration; 3 | 4 | // atomic duration in milli seconds 5 | #[derive(Debug)] 6 | pub struct AtomicDuration(AtomicUsize); 7 | 8 | impl AtomicDuration { 9 | pub fn new(dur: Option) -> Self { 10 | let dur = match dur { 11 | None => 0, 12 | Some(d) => dur_to_ms(d) as usize, 13 | }; 14 | AtomicDuration(AtomicUsize::new(dur)) 15 | } 16 | 17 | #[inline] 18 | pub fn get(&self) -> Option { 19 | match self.0.load(Ordering::Relaxed) { 20 | 0 => None, 21 | d => Some(Duration::from_millis(d as u64)), 22 | } 23 | } 24 | 25 | #[inline] 26 | pub fn store(&self, dur: Option) { 27 | let timeout = match dur { 28 | None => 0, 29 | Some(d) => dur_to_ms(d) as usize, 30 | }; 31 | 32 | self.0.store(timeout, Ordering::Relaxed); 33 | } 34 | 35 | #[inline] 36 | pub fn take(&self) -> Option { 37 | match self.0.swap(0, Ordering::Relaxed) { 38 | 0 => None, 39 | d => Some(Duration::from_millis(d as u64)), 40 | } 41 | } 42 | 43 | #[inline] 44 | pub fn into_inner(self) -> Option { 45 | self.take() 46 | } 47 | } 48 | 49 | fn dur_to_ms(dur: Duration) -> u64 { 50 | // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair 51 | const MS_PER_SEC: u64 = 1_000; 52 | const NANOS_PER_MILLI: u64 = 1_000_000; 53 | let ns = u64::from(dur.subsec_nanos()); 54 | let ms = (ns + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI; 55 | dur.as_secs().saturating_mul(MS_PER_SEC).saturating_add(ms) 56 | } 57 | -------------------------------------------------------------------------------- /src/guard.rs: -------------------------------------------------------------------------------- 1 | use crate::{Manager, Pool}; 2 | use std::fmt::{Debug, Formatter}; 3 | use std::ops::{Deref, DerefMut}; 4 | use std::sync::atomic::Ordering; 5 | 6 | /// ConnectionGuard is a wrapper for Connection 7 | pub struct ConnectionGuard { 8 | pub inner: Option, 9 | pool: Pool, 10 | checked: bool, 11 | } 12 | 13 | impl ConnectionGuard { 14 | pub fn new(conn: M::Connection, pool: Pool) -> ConnectionGuard { 15 | Self { 16 | inner: Some(conn), 17 | pool, 18 | checked: false, 19 | } 20 | } 21 | 22 | pub fn set_checked(&mut self, checked: bool) { 23 | self.checked = checked; 24 | if checked { 25 | self.pool.in_use.fetch_add(1, Ordering::SeqCst); 26 | } 27 | } 28 | } 29 | 30 | impl Debug for ConnectionGuard { 31 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 32 | f.debug_struct("ConnectionGuard") 33 | .field("pool", &self.pool) 34 | .finish() 35 | } 36 | } 37 | 38 | impl Deref for ConnectionGuard { 39 | type Target = M::Connection; 40 | 41 | fn deref(&self) -> &Self::Target { 42 | self.inner.as_ref().unwrap() 43 | } 44 | } 45 | 46 | impl DerefMut for ConnectionGuard { 47 | fn deref_mut(&mut self) -> &mut Self::Target { 48 | self.inner.as_mut().unwrap() 49 | } 50 | } 51 | 52 | impl Drop for ConnectionGuard { 53 | fn drop(&mut self) { 54 | if self.checked == false { 55 | if self.pool.connections.load(Ordering::SeqCst) > 0 { 56 | self.pool.connections.fetch_sub(1, Ordering::SeqCst); 57 | } 58 | } else { 59 | if let Some(v) = self.inner.take() { 60 | _ = self.pool.recycle(v); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(async_fn_in_trait)] 2 | 3 | #[macro_use] 4 | mod defer; 5 | mod duration; 6 | pub mod state; 7 | pub mod guard; 8 | pub mod pool; 9 | pub mod plugin; 10 | 11 | /// Manager create Connection and check Connection 12 | pub trait Manager { 13 | type Connection; 14 | 15 | type Error: for<'a> From<&'a str>; 16 | 17 | ///create Connection and check Connection 18 | async fn connect(&self) -> Result; 19 | ///check Connection is alive? if not return Error(Connection will be drop) 20 | async fn check(&self, conn: &mut Self::Connection) -> Result<(), Self::Error>; 21 | } 22 | 23 | pub use pool::Pool; 24 | pub use guard::ConnectionGuard; 25 | pub use state::State; 26 | 27 | -------------------------------------------------------------------------------- /src/plugin/check_duration_manager.rs: -------------------------------------------------------------------------------- 1 | use std::time::{Duration, SystemTime, UNIX_EPOCH}; 2 | use crate::duration::AtomicDuration; 3 | use crate::Manager; 4 | 5 | /// `CheckDurationConnectionManager` is a manager wrapper that implements connection validation 6 | /// based on a specified idle duration. 7 | /// 8 | /// This manager only performs actual connection validation after a specified duration has passed 9 | /// since the last check. This can significantly reduce the overhead of frequent validation 10 | /// for connections that are used repeatedly within short time intervals. 11 | /// 12 | /// # Example 13 | /// ```no_run 14 | /// use std::time::Duration; 15 | /// use fast_pool::{Manager, Pool}; 16 | /// use fast_pool::plugin::CheckDurationConnectionManager; 17 | /// 18 | /// // Assume we have some database manager that implements Manager trait 19 | /// struct MyDatabaseManager; 20 | /// 21 | /// impl Manager for MyDatabaseManager { 22 | /// type Connection = (); 23 | /// type Error = String; 24 | /// 25 | /// async fn connect(&self) -> Result { 26 | /// Ok(()) 27 | /// } 28 | /// 29 | /// async fn check(&self, _conn: &mut Self::Connection) -> Result<(), Self::Error> { 30 | /// Ok(()) 31 | /// } 32 | /// } 33 | /// 34 | /// let base_manager = MyDatabaseManager; 35 | /// let duration_manager = CheckDurationConnectionManager::new(base_manager, Duration::from_secs(60)); 36 | /// let pool = Pool::new(duration_manager); 37 | /// ``` 38 | pub struct CheckDurationConnectionManager { 39 | /// The underlying connection manager that handles the actual connection operations 40 | pub manager: M, 41 | /// Minimum duration between actual connection checks 42 | pub duration: Duration, 43 | /// Timestamp of the last actual check (stored as duration since UNIX epoch) 44 | pub instant: AtomicDuration, 45 | } 46 | 47 | impl CheckDurationConnectionManager { 48 | /// Creates a new `CheckDurationConnectionManager` with the specified manager and check duration. 49 | /// 50 | /// # Parameters 51 | /// - `manager`: The underlying connection manager 52 | /// - `duration`: The minimum duration that must pass before performing an actual check 53 | /// 54 | /// # Returns 55 | /// A new `CheckDurationConnectionManager` instance 56 | pub fn new(manager: M, duration: Duration) -> Self { 57 | Self { 58 | manager, 59 | duration, 60 | instant: AtomicDuration::new(Some(Duration::from_secs(0))), 61 | } 62 | } 63 | } 64 | 65 | impl Manager for CheckDurationConnectionManager { 66 | type Connection = M::Connection; 67 | type Error = M::Error; 68 | 69 | /// Creates a new connection by delegating to the underlying manager. 70 | /// 71 | /// This method simply forwards the connection request to the wrapped manager. 72 | async fn connect(&self) -> Result { 73 | self.manager.connect().await 74 | } 75 | 76 | /// Checks if a connection is still valid, but only performs actual validation 77 | /// if the specified duration has passed since the last check. 78 | /// 79 | /// # Logic 80 | /// 1. Get the current time (as duration since UNIX epoch) 81 | /// 2. Get the timestamp of the last check 82 | /// 3. If less than `duration` time has passed, assume the connection is valid 83 | /// 4. Otherwise, perform an actual check using the underlying manager and update the timestamp 84 | /// 85 | /// # Parameters 86 | /// - `conn`: The connection to check 87 | /// 88 | /// # Returns 89 | /// - `Ok(())` if the connection is valid or if the duration hasn't passed 90 | /// - The error from the underlying manager if the check fails 91 | async fn check(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { 92 | let now = SystemTime::now() 93 | .duration_since(UNIX_EPOCH) 94 | .unwrap_or(Duration::from_secs(0)); 95 | let last_check = self.instant.get().unwrap_or_default(); 96 | // Skip the actual check if not enough time has passed 97 | if now.saturating_sub(last_check) < self.duration { 98 | return Ok(()); 99 | } 100 | // Update the timestamp and perform the actual check 101 | self.instant.store(Some(now)); 102 | self.manager.check(conn).await 103 | } 104 | } -------------------------------------------------------------------------------- /src/plugin/mod.rs: -------------------------------------------------------------------------------- 1 | mod check_duration_manager; 2 | pub use check_duration_manager::*; -------------------------------------------------------------------------------- /src/pool.rs: -------------------------------------------------------------------------------- 1 | use crate::guard::ConnectionGuard; 2 | use crate::state::State; 3 | use crate::Manager; 4 | use flume::{Receiver, Sender}; 5 | use std::fmt::{Debug, Formatter}; 6 | use std::sync::atomic::{AtomicU64, Ordering}; 7 | use std::sync::Arc; 8 | use std::time::Duration; 9 | use crate::duration::AtomicDuration; 10 | 11 | /// Pool have manager, get/get_timeout Connection from Pool 12 | pub struct Pool { 13 | pub manager: Arc, 14 | pub idle_send: Arc>, 15 | pub idle_recv: Arc>, 16 | /// max open connection default 32 17 | pub max_open: Arc, 18 | pub(crate) in_use: Arc, 19 | pub(crate) waits: Arc, 20 | pub(crate) connecting: Arc, 21 | pub(crate) checking: Arc, 22 | pub(crate) connections: Arc, 23 | //timeout check connection default 10s 24 | pub timeout_check: Arc, 25 | } 26 | 27 | impl Debug for Pool { 28 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 29 | let state = self.state(); 30 | Debug::fmt(&state, f) 31 | } 32 | } 33 | 34 | impl Clone for Pool { 35 | fn clone(&self) -> Self { 36 | Self { 37 | manager: self.manager.clone(), 38 | idle_send: self.idle_send.clone(), 39 | idle_recv: self.idle_recv.clone(), 40 | max_open: self.max_open.clone(), 41 | in_use: self.in_use.clone(), 42 | waits: self.waits.clone(), 43 | connecting: self.connecting.clone(), 44 | checking: self.checking.clone(), 45 | connections: self.connections.clone(), 46 | timeout_check: self.timeout_check.clone(), 47 | } 48 | } 49 | } 50 | 51 | impl Pool { 52 | pub fn new(m: M) -> Self 53 | where 54 | M::Connection: Unpin, 55 | { 56 | let (s, r) = flume::unbounded(); 57 | Self { 58 | manager: Arc::new(m), 59 | idle_send: Arc::new(s), 60 | idle_recv: Arc::new(r), 61 | max_open: Arc::new(AtomicU64::new(32)), 62 | in_use: Arc::new(AtomicU64::new(0)), 63 | waits: Arc::new(AtomicU64::new(0)), 64 | connecting: Arc::new(AtomicU64::new(0)), 65 | checking: Arc::new(AtomicU64::new(0)), 66 | connections: Arc::new(AtomicU64::new(0)), 67 | timeout_check: Arc::new(AtomicDuration::new(Some(Duration::from_secs(10)))), 68 | } 69 | } 70 | 71 | pub async fn get(&self) -> Result, M::Error> { 72 | self.get_timeout(None).await 73 | } 74 | 75 | pub async fn get_timeout(&self, d: Option) -> Result, M::Error> { 76 | self.waits.fetch_add(1, Ordering::SeqCst); 77 | defer!(|| { 78 | self.waits.fetch_sub(1, Ordering::SeqCst); 79 | }); 80 | let f = async { 81 | let v: Result, M::Error> = loop { 82 | let connections = self.connections.load(Ordering::SeqCst) 83 | + self.connecting.load(Ordering::SeqCst); 84 | if connections < self.max_open.load(Ordering::SeqCst) { 85 | //Use In_use placeholder when create connection 86 | self.connecting.fetch_add(1, Ordering::SeqCst); 87 | defer!(|| { 88 | self.connecting.fetch_sub(1, Ordering::SeqCst); 89 | }); 90 | //create connection,this can limit max idle,current now max idle = max_open 91 | let conn = self.manager.connect().await?; 92 | self.idle_send 93 | .send(conn) 94 | .map_err(|e| M::Error::from(&e.to_string()))?; 95 | self.connections.fetch_add(1, Ordering::SeqCst); 96 | } 97 | let conn = self 98 | .idle_recv 99 | .recv_async() 100 | .await 101 | .map_err(|e| M::Error::from(&e.to_string()))?; 102 | let mut guard = ConnectionGuard::new(conn, self.clone()); 103 | guard.set_checked(false); 104 | //check connection 105 | self.checking.fetch_add(1, Ordering::SeqCst); 106 | defer!(|| { 107 | self.checking.fetch_sub(1, Ordering::SeqCst); 108 | }); 109 | let check_result = tokio::time::timeout( 110 | self.timeout_check.get().unwrap_or_default(), 111 | self.manager.check(&mut guard), 112 | ) 113 | .await 114 | .map_err(|e| M::Error::from(&format!("check_timeout={}", e)))?; 115 | match check_result { 116 | Ok(_) => { 117 | guard.set_checked(true); 118 | break Ok(guard); 119 | } 120 | Err(_e) => { 121 | drop(guard); 122 | continue; 123 | } 124 | } 125 | }; 126 | v 127 | }; 128 | let conn = { 129 | match d { 130 | None => {f.await?} 131 | Some(duration) => { 132 | tokio::time::timeout(duration, f) 133 | .await 134 | .map_err(|_e| M::Error::from("get_timeout"))?? 135 | } 136 | } 137 | }; 138 | Ok(conn) 139 | } 140 | 141 | pub fn state(&self) -> State { 142 | State { 143 | max_open: self.max_open.load(Ordering::Relaxed), 144 | connections: self.connections.load(Ordering::Relaxed), 145 | in_use: self.in_use.load(Ordering::SeqCst), 146 | idle: self.idle_send.len() as u64, 147 | waits: self.waits.load(Ordering::SeqCst), 148 | connecting: self.connecting.load(Ordering::SeqCst), 149 | checking: self.checking.load(Ordering::SeqCst), 150 | } 151 | } 152 | 153 | pub fn set_max_open(&self, n: u64) { 154 | if n == 0 { 155 | return; 156 | } 157 | self.max_open.store(n, Ordering::SeqCst); 158 | loop { 159 | if self.idle_send.len() > n as usize { 160 | _ = self.idle_recv.try_recv(); 161 | if self.connections.load(Ordering::SeqCst) > 0 { 162 | self.connections.fetch_sub(1, Ordering::SeqCst); 163 | } 164 | } else { 165 | break; 166 | } 167 | } 168 | } 169 | 170 | pub fn get_max_open(&self) -> u64 { 171 | self.max_open.load(Ordering::SeqCst) 172 | } 173 | 174 | pub fn recycle(&self, arg: M::Connection) { 175 | self.in_use.fetch_sub(1, Ordering::SeqCst); 176 | if self.idle_send.len() < self.max_open.load(Ordering::SeqCst) as usize { 177 | _ = self.idle_send.send(arg); 178 | } else { 179 | if self.connections.load(Ordering::SeqCst) > 0 { 180 | self.connections.fetch_sub(1, Ordering::SeqCst); 181 | } 182 | } 183 | } 184 | 185 | /// Set the timeout for checking connections in the pool. 186 | pub fn set_timeout_check(&self, duration: Option) { 187 | self.timeout_check.store(duration); 188 | } 189 | 190 | /// Set the timeout for checking connections in the pool. 191 | pub fn get_timeout_check(&self) -> Option { 192 | self.timeout_check.get() 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/state.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter}; 2 | 3 | #[derive(Debug, Eq, PartialEq)] 4 | pub struct State { 5 | /// max open limit 6 | pub max_open: u64, 7 | /// connections = in_use number + idle number + connecting number 8 | pub connections: u64, 9 | /// user use connection number 10 | pub in_use: u64, 11 | /// idle connection 12 | pub idle: u64, 13 | /// wait get connections number 14 | pub waits: u64, 15 | /// connecting connections 16 | pub connecting: u64, 17 | /// checking connections 18 | pub checking: u64, 19 | } 20 | 21 | impl Display for State { 22 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 23 | write!( 24 | f, 25 | "{{ max_open: {}, connections: {}, in_use: {}, idle: {}, connecting: {}, checking: {}, waits: {} }}", 26 | self.max_open, self.connections, self.in_use, self.idle, self.connecting, self.checking, self.waits 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/duration_manager_test.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | use std::ops::{Deref, DerefMut}; 3 | use std::time::Duration; 4 | use fast_pool::{Manager, Pool}; 5 | use std::sync::Arc; 6 | use std::sync::atomic::{AtomicUsize, Ordering}; 7 | 8 | // Import the test connection and manager from pool_test.rs 9 | #[derive(Debug,Clone)] 10 | pub struct TestManager {} 11 | 12 | #[derive(Debug, Clone)] 13 | pub struct TestConnection { 14 | pub inner: String, 15 | } 16 | 17 | impl TestConnection { 18 | pub fn new() -> TestConnection { 19 | TestConnection { 20 | inner: "".to_string(), 21 | } 22 | } 23 | } 24 | 25 | impl Display for TestConnection { 26 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 27 | write!(f, "{}", self.inner) 28 | } 29 | } 30 | 31 | impl Deref for TestConnection { 32 | type Target = String; 33 | 34 | fn deref(&self) -> &Self::Target { 35 | &self.inner 36 | } 37 | } 38 | 39 | impl DerefMut for TestConnection { 40 | fn deref_mut(&mut self) -> &mut Self::Target { 41 | &mut self.inner 42 | } 43 | } 44 | 45 | impl Manager for TestManager { 46 | type Connection = TestConnection; 47 | type Error = String; 48 | 49 | async fn connect(&self) -> Result { 50 | Ok(TestConnection::new()) 51 | } 52 | 53 | async fn check(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { 54 | if conn.inner != "" { 55 | return Err(Self::Error::from(&conn.to_string())); 56 | } 57 | Ok(()) 58 | } 59 | } 60 | 61 | /// Wrapper for TestManager to track check calls 62 | #[derive(Clone)] 63 | pub struct CheckCounterManager { 64 | manager: TestManager, 65 | check_count: Arc, 66 | } 67 | 68 | impl CheckCounterManager { 69 | fn new() -> Self { 70 | Self { 71 | manager: TestManager{}, 72 | check_count: Arc::new(AtomicUsize::new(0)), 73 | } 74 | } 75 | 76 | fn get_check_count(&self) -> usize { 77 | self.check_count.load(Ordering::SeqCst) 78 | } 79 | } 80 | 81 | impl Manager for CheckCounterManager { 82 | type Connection = TestConnection; 83 | type Error = String; 84 | 85 | async fn connect(&self) -> Result { 86 | self.manager.connect().await 87 | } 88 | 89 | async fn check(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { 90 | self.check_count.fetch_add(1, Ordering::SeqCst); 91 | self.manager.check(conn).await 92 | } 93 | } 94 | 95 | /// CheckDurationConnectionManager implementation 96 | struct CheckDurationConnectionManager { 97 | pub manager: M, 98 | pub duration: Duration, 99 | pub last_check: std::sync::Mutex>, 100 | } 101 | 102 | impl CheckDurationConnectionManager { 103 | fn new(manager: M, duration: Duration) -> Self { 104 | Self { 105 | manager, 106 | duration, 107 | last_check: std::sync::Mutex::new(None), 108 | } 109 | } 110 | } 111 | 112 | impl Manager for CheckDurationConnectionManager { 113 | type Connection = M::Connection; 114 | type Error = M::Error; 115 | 116 | async fn connect(&self) -> Result { 117 | self.manager.connect().await 118 | } 119 | 120 | async fn check(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { 121 | let now = std::time::SystemTime::now(); 122 | let should_check = { 123 | let mut last_check = self.last_check.lock().unwrap(); 124 | let should_check = match *last_check { 125 | None => true, 126 | Some(time) => { 127 | match now.duration_since(time) { 128 | Ok(elapsed) => elapsed >= self.duration, 129 | Err(_) => true, // Clock went backwards, check to be safe 130 | } 131 | } 132 | }; 133 | 134 | if should_check { 135 | *last_check = Some(now); 136 | } 137 | 138 | should_check 139 | }; 140 | 141 | if should_check { 142 | self.manager.check(conn).await 143 | } else { 144 | Ok(()) 145 | } 146 | } 147 | } 148 | 149 | #[tokio::test] 150 | async fn test_check_duration_manager_basic() { 151 | let counter_manager = CheckCounterManager::new(); 152 | // Create a duration manager that only checks every 500ms 153 | let duration_manager = CheckDurationConnectionManager::new(counter_manager.clone(), Duration::from_millis(500)); 154 | let pool = Pool::new(duration_manager); 155 | 156 | // Get a connection and immediately return it to the pool 157 | let conn = pool.get().await.unwrap(); 158 | drop(conn); 159 | assert_eq!(counter_manager.get_check_count(), 1, "First check should happen"); 160 | 161 | // Get another connection immediately - should not trigger a check 162 | let conn = pool.get().await.unwrap(); 163 | drop(conn); 164 | assert_eq!(counter_manager.get_check_count(), 1, "Second immediate check should be skipped"); 165 | 166 | // Wait for duration to expire 167 | tokio::time::sleep(Duration::from_millis(550)).await; 168 | 169 | // Get a connection after duration - should trigger a check 170 | let conn = pool.get().await.unwrap(); 171 | drop(conn); 172 | assert_eq!(counter_manager.get_check_count(), 2, "Check should happen after duration"); 173 | } 174 | 175 | #[tokio::test] 176 | async fn test_check_duration_manager_concurrent() { 177 | let counter_manager = CheckCounterManager::new(); 178 | let check_count_tracker = counter_manager.check_count.clone(); 179 | 180 | // Create a duration manager with a longer check interval 181 | let duration_manager = CheckDurationConnectionManager::new(counter_manager, Duration::from_millis(200)); 182 | let pool = Pool::new(duration_manager); 183 | 184 | // Get and release 10 connections rapidly 185 | for _ in 0..10 { 186 | let conn = pool.get().await.unwrap(); 187 | drop(conn); 188 | } 189 | 190 | // Only one check should have happened despite 10 connections 191 | assert_eq!(check_count_tracker.load(Ordering::SeqCst), 1, 192 | "Multiple rapid connections should only trigger one check"); 193 | 194 | // Wait for duration to expire 195 | tokio::time::sleep(Duration::from_millis(250)).await; 196 | 197 | // Get one more connection after waiting 198 | let conn = pool.get().await.unwrap(); 199 | drop(conn); 200 | assert_eq!(check_count_tracker.load(Ordering::SeqCst), 2, 201 | "Check should happen after duration expires"); 202 | } 203 | 204 | #[tokio::test] 205 | async fn test_check_duration_manager_invalid_connection() { 206 | let counter_manager = CheckCounterManager::new(); 207 | let check_count_tracker = counter_manager.check_count.clone(); 208 | 209 | // Create a duration manager with a moderate check interval 210 | let duration_manager = CheckDurationConnectionManager::new(counter_manager, Duration::from_millis(100)); 211 | let pool = Pool::new(duration_manager); 212 | 213 | // Get a connection and make it invalid 214 | let mut conn = pool.get().await.unwrap(); 215 | (*conn).inner = "invalid".to_string(); 216 | drop(conn); // Return invalid connection to pool 217 | 218 | // The first check should have happened 219 | assert_eq!(check_count_tracker.load(Ordering::SeqCst), 1); 220 | 221 | // Get a new connection immediately - shouldn't trigger another check due to duration 222 | // NOTE: Since our manager skips the check, the invalid connection will be returned 223 | // without validation. This is expected behavior for the CheckDurationConnectionManager! 224 | let conn = pool.get().await.unwrap(); 225 | 226 | // We expect the connection to still be invalid since checks are being skipped 227 | assert_eq!(conn.inner.as_ref().unwrap().inner, "invalid", 228 | "Connection should still be invalid since check was skipped"); 229 | 230 | // Check counter should remain the same since we're within the duration 231 | assert_eq!(check_count_tracker.load(Ordering::SeqCst), 1, 232 | "Check should be skipped due to duration limit"); 233 | 234 | // Now wait for duration to pass 235 | tokio::time::sleep(Duration::from_millis(150)).await; 236 | 237 | // Return the invalid connection 238 | drop(conn); 239 | 240 | // Get another connection, now it should be checked and fixed 241 | let conn = pool.get().await.unwrap(); 242 | // Now the connection should be valid because the check should have happened 243 | // due to time expiration 244 | assert_eq!(check_count_tracker.load(Ordering::SeqCst), 2, 245 | "Check should happen after duration expires"); 246 | 247 | // The connection should be valid now 248 | assert_eq!(conn.inner.as_ref().unwrap().inner, "", 249 | "Connection should be valid after check is performed"); 250 | } -------------------------------------------------------------------------------- /tests/pool_test.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | use fast_pool::{Manager, Pool}; 3 | use std::ops::{Deref, DerefMut}; 4 | use std::time::Duration; 5 | use std::sync::Arc; 6 | use std::sync::atomic::{AtomicUsize, Ordering}; 7 | 8 | #[derive(Debug)] 9 | pub struct TestManager {} 10 | 11 | 12 | #[derive(Debug,Clone)] 13 | pub struct TestConnection{ 14 | pub inner: String, 15 | } 16 | 17 | impl TestConnection { 18 | pub fn new()->TestConnection{ 19 | println!("new Connection"); 20 | TestConnection{ 21 | inner: "".to_string(), 22 | } 23 | } 24 | } 25 | 26 | impl Display for TestConnection{ 27 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 28 | write!(f, "{}", self.inner) 29 | } 30 | } 31 | impl Deref for TestConnection { 32 | type Target = String; 33 | 34 | fn deref(&self) -> &Self::Target { 35 | &self.inner 36 | } 37 | } 38 | 39 | impl DerefMut for TestConnection { 40 | fn deref_mut(&mut self) -> &mut Self::Target { 41 | &mut self.inner 42 | } 43 | } 44 | 45 | impl Drop for TestConnection { 46 | fn drop(&mut self) { 47 | println!("drop Connection"); 48 | } 49 | } 50 | 51 | 52 | impl Manager for TestManager { 53 | type Connection = TestConnection; 54 | type Error = String; 55 | 56 | async fn connect(&self) -> Result { 57 | Ok(TestConnection::new()) 58 | } 59 | 60 | async fn check(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { 61 | if conn.inner != "" { 62 | return Err(Self::Error::from(&conn.to_string())); 63 | } 64 | Ok(()) 65 | } 66 | } 67 | 68 | #[tokio::test] 69 | async fn test_debug() { 70 | let p = Pool::new(TestManager {}); 71 | println!("{:?}", p); 72 | } 73 | 74 | #[tokio::test] 75 | async fn test_clone() { 76 | let p = Pool::new(TestManager {}); 77 | let p2 = p.clone(); 78 | assert_eq!(p.state(), p2.state()); 79 | } 80 | 81 | // --nocapture 82 | #[tokio::test] 83 | async fn test_pool_get() { 84 | let p = Pool::new(TestManager {}); 85 | p.set_max_open(10); 86 | let mut arr = vec![]; 87 | for i in 0..10 { 88 | let v = p.get().await.unwrap(); 89 | println!("{},{}", i, v.deref()); 90 | arr.push(v); 91 | } 92 | } 93 | 94 | #[tokio::test] 95 | async fn test_pool_get2() { 96 | let p = Pool::new(TestManager {}); 97 | p.set_max_open(10); 98 | for i in 0..3 { 99 | let v = p.get().await.unwrap(); 100 | println!("{},{}", i, v.deref().inner.as_str()); 101 | } 102 | assert_eq!(p.state().idle, 3); 103 | } 104 | 105 | #[tokio::test] 106 | async fn test_pool_get_timeout() { 107 | let p = Pool::new(TestManager {}); 108 | p.set_max_open(10); 109 | let mut arr = vec![]; 110 | for i in 0..10 { 111 | let v = p.get().await.unwrap(); 112 | println!("{},{}", i, v.deref()); 113 | arr.push(v); 114 | } 115 | assert_eq!( 116 | p.get_timeout(Some(Duration::from_secs(0))).await.is_err(), 117 | true 118 | ); 119 | } 120 | 121 | #[tokio::test] 122 | async fn test_pool_check() { 123 | let p = Pool::new(TestManager {}); 124 | p.set_max_open(10); 125 | let mut v = p.get().await.unwrap(); 126 | *v.inner.as_mut().unwrap() = TestConnection{inner:"error".to_string()}; 127 | for _i in 0..10 { 128 | let v = p.get().await.unwrap(); 129 | assert_eq!(v.deref().inner == "error", false); 130 | } 131 | } 132 | 133 | #[tokio::test] 134 | async fn test_pool_resize() { 135 | let p = Pool::new(TestManager {}); 136 | p.set_max_open(10); 137 | let mut arr = vec![]; 138 | for i in 0..10 { 139 | let v = p.get().await.unwrap(); 140 | println!("{},{}", i, v.deref()); 141 | arr.push(v); 142 | } 143 | assert_eq!( 144 | p.get_timeout(Some(Duration::from_secs(0))).await.is_err(), 145 | true 146 | ); 147 | p.set_max_open(11); 148 | assert_eq!( 149 | p.get_timeout(Some(Duration::from_secs(0))).await.is_err(), 150 | false 151 | ); 152 | arr.push(p.get().await.unwrap()); 153 | assert_eq!( 154 | p.get_timeout(Some(Duration::from_secs(0))).await.is_err(), 155 | true 156 | ); 157 | } 158 | 159 | #[tokio::test] 160 | async fn test_pool_resize2() { 161 | let p = Pool::new(TestManager {}); 162 | p.set_max_open(2); 163 | let mut arr = vec![]; 164 | for _i in 0..2 { 165 | let v = p.get().await.unwrap(); 166 | arr.push(v); 167 | } 168 | p.set_max_open(1); 169 | drop(arr); 170 | println!("{:?}", p.state()); 171 | assert_eq!( 172 | p.get_timeout(Some(Duration::from_secs(0))).await.is_err(), 173 | false 174 | ); 175 | } 176 | 177 | #[tokio::test] 178 | async fn test_concurrent_access() { 179 | let p = Pool::new(TestManager {}); 180 | p.set_max_open(10); 181 | let mut handles = vec![]; 182 | for _ in 0..10 { 183 | let pool = p.clone(); 184 | let handle = tokio::spawn(async move { 185 | let _ = pool.get().await.unwrap(); 186 | }); 187 | handles.push(handle); 188 | } 189 | for handle in handles { 190 | handle.await.unwrap(); 191 | } 192 | assert_eq!(p.state().connections, 10); 193 | } 194 | 195 | #[tokio::test] 196 | async fn test_invalid_connection() { 197 | let p = Pool::new(TestManager {}); 198 | p.set_max_open(1); 199 | 200 | let mut conn = p.get().await.unwrap(); 201 | //conn timeout 202 | *conn.inner.as_mut().unwrap() = TestConnection{inner: "error".to_string()}; 203 | drop(conn); 204 | println!("pool state: {}", p.state()); 205 | // Attempt to get a new connection, should not be the invalid one 206 | let new_conn = p.get().await.unwrap(); 207 | assert_ne!(new_conn.deref().inner, "error".to_string()); 208 | } 209 | 210 | #[tokio::test] 211 | async fn test_connection_lifetime() { 212 | let p = Pool::new(TestManager {}); 213 | p.set_max_open(10); 214 | 215 | let conn = p.get().await.unwrap(); 216 | // Perform operations using the connection 217 | // ... 218 | 219 | drop(conn); // Drop the connection 220 | 221 | // Ensure dropped connection is not in use 222 | assert_eq!(p.state().in_use, 0); 223 | 224 | // Acquire a new connection 225 | let new_conn = p.get().await.unwrap(); 226 | assert_ne!(new_conn.deref().inner, "error".to_string()); 227 | } 228 | 229 | #[tokio::test] 230 | async fn test_boundary_conditions() { 231 | let p = Pool::new(TestManager {}); 232 | p.set_max_open(2); 233 | 234 | // Acquire connections until pool is full 235 | let conn_1 = p.get().await.unwrap(); 236 | let _conn_2 = p.get().await.unwrap(); 237 | println!("{}", p.state()); 238 | assert_eq!(p.state().in_use, 2); 239 | 240 | // Attempt to acquire another connection (pool is full) 241 | assert!(p.get_timeout(Some(Duration::from_secs(0))).await.is_err()); 242 | 243 | // Release one connection, pool is no longer full 244 | drop(conn_1); 245 | assert_eq!(p.state().in_use, 1); 246 | 247 | // Acquire another connection (pool has space) 248 | let _conn_3 = p.get().await.unwrap(); 249 | assert_eq!(p.state().in_use, 2); 250 | 251 | // Increase pool size 252 | p.set_max_open(3); 253 | // Acquire another connection after increasing pool size 254 | let _conn_4 = p.get().await.unwrap(); 255 | assert_eq!(p.state().in_use, 3); 256 | } 257 | 258 | #[tokio::test] 259 | async fn test_pool_wait() { 260 | let p = Pool::new(TestManager {}); 261 | p.set_max_open(1); 262 | let v = p.get().await.unwrap(); 263 | let p1 = p.clone(); 264 | tokio::spawn(async move { 265 | p1.get().await.unwrap(); 266 | drop(p1); 267 | }); 268 | let p1 = p.clone(); 269 | tokio::spawn(async move { 270 | p1.get().await.unwrap(); 271 | drop(p1); 272 | }); 273 | tokio::time::sleep(Duration::from_secs(1)).await; 274 | println!("{:?}", p.state()); 275 | assert_eq!(p.state().waits, 2); 276 | drop(v); 277 | } 278 | 279 | #[tokio::test] 280 | async fn test_high_concurrency_with_timeout() { 281 | // Create a pool with small connection limit 282 | let p = Pool::new(TestManager {}); 283 | p.set_max_open(5); 284 | 285 | // Counter for successful connection acquisition 286 | let success_count = Arc::new(AtomicUsize::new(0)); 287 | // Counter for timeout events 288 | let timeout_count = Arc::new(AtomicUsize::new(0)); 289 | 290 | // Create many concurrent tasks, exceeding pool capacity 291 | let mut handles = vec![]; 292 | let task_count = 50; 293 | 294 | for _ in 0..task_count { 295 | let pool = p.clone(); 296 | let success = success_count.clone(); 297 | let timeout = timeout_count.clone(); 298 | 299 | let handle = tokio::spawn(async move { 300 | // Use very short timeout to simulate high pressure 301 | match pool.get_timeout(Some(Duration::from_millis(50))).await { 302 | Ok(conn) => { 303 | // Successfully got connection 304 | success.fetch_add(1, Ordering::SeqCst); 305 | // Simulate brief connection usage 306 | tokio::time::sleep(Duration::from_millis(20)).await; 307 | // Return connection to pool 308 | drop(conn); 309 | } 310 | Err(_) => { 311 | // Connection acquisition timed out 312 | timeout.fetch_add(1, Ordering::SeqCst); 313 | } 314 | } 315 | }); 316 | handles.push(handle); 317 | } 318 | 319 | // Wait for all tasks to complete 320 | for handle in handles { 321 | handle.await.unwrap(); 322 | } 323 | 324 | // Verify pool state 325 | println!("Final pool state: {:?}", p.state()); 326 | println!("Successful connections: {}", success_count.load(Ordering::SeqCst)); 327 | println!("Timeouts: {}", timeout_count.load(Ordering::SeqCst)); 328 | 329 | // Verify success + timeout equals total tasks 330 | assert_eq!( 331 | success_count.load(Ordering::SeqCst) + timeout_count.load(Ordering::SeqCst), 332 | task_count 333 | ); 334 | 335 | // Verify pool connections did not exceed limit 336 | assert!(p.state().connections <= p.state().max_open); 337 | 338 | // Wait for connections to return to pool 339 | tokio::time::sleep(Duration::from_millis(100)).await; 340 | 341 | // Pool should be in idle state, all connections in idle queue 342 | assert_eq!(p.state().in_use, 0); 343 | assert!(p.state().idle <= p.state().max_open); 344 | } 345 | 346 | #[tokio::test] 347 | async fn test_concurrent_create_connection() { 348 | // Create a connection pool with specific connection limit 349 | let p = Pool::new(TestManager {}); 350 | let max_connections = 10; 351 | p.set_max_open(max_connections); 352 | 353 | // Clear the connection pool 354 | p.set_max_open(0); 355 | p.set_max_open(max_connections); 356 | 357 | // Number of concurrent tasks, several times the pool limit 358 | let tasks = 30; 359 | let mut handles = vec![]; 360 | 361 | // Concurrently start multiple tasks all trying to get connections 362 | for i in 0..tasks { 363 | let pool = p.clone(); 364 | let handle = tokio::spawn(async move { 365 | let result = pool.get().await; 366 | println!("Task {} get connection: {}", i, result.is_ok()); 367 | result 368 | }); 369 | handles.push(handle); 370 | } 371 | 372 | // Collect results 373 | let mut success_count = 0; 374 | for handle in handles { 375 | if handle.await.unwrap().is_ok() { 376 | success_count += 1; 377 | } 378 | } 379 | 380 | // Wait for connections to return to pool 381 | tokio::time::sleep(Duration::from_millis(100)).await; 382 | 383 | println!("Pool state: {:?}", p.state()); 384 | println!("Successfully created connections: {}", success_count); 385 | 386 | // Verify pool did not exceed max connections 387 | assert!(p.state().connections <= max_connections); 388 | 389 | // All active connections should be returned to pool 390 | assert_eq!(p.state().in_use, 0); 391 | 392 | // Verify idle connections don't exceed max 393 | assert!(p.state().idle <= max_connections); 394 | } 395 | 396 | #[tokio::test] 397 | async fn test_high_concurrency_long_connections() { 398 | // Create a connection pool with a specific limit 399 | let p = Pool::new(TestManager {}); 400 | let max_connections = 20; // Maximum number of connections allowed 401 | p.set_max_open(max_connections); 402 | 403 | // Clear the connection pool 404 | p.set_max_open(0); 405 | p.set_max_open(max_connections); 406 | 407 | // Simulate a high number of concurrent requests 408 | let task_count = 200; // Reduced for faster test execution 409 | let connection_duration = Duration::from_secs(3); // Each connection lives for 3 seconds 410 | 411 | let success_count = Arc::new(AtomicUsize::new(0)); 412 | let timeout_count = Arc::new(AtomicUsize::new(0)); 413 | let in_progress = Arc::new(AtomicUsize::new(0)); 414 | let max_in_progress = Arc::new(AtomicUsize::new(0)); 415 | 416 | // Track the maximum number of concurrent connections 417 | let update_max = |current: usize, max_tracker: &Arc| { 418 | let mut current_max = max_tracker.load(Ordering::Relaxed); 419 | while current > current_max { 420 | match max_tracker.compare_exchange_weak( 421 | current_max, 422 | current, 423 | Ordering::SeqCst, 424 | Ordering::Relaxed, 425 | ) { 426 | Ok(_) => break, 427 | Err(val) => current_max = val, 428 | } 429 | } 430 | }; 431 | 432 | println!("Starting high concurrency test with long-lived connections"); 433 | println!("Max connections: {}, Tasks: {}, Connection duration: {:?}", 434 | max_connections, task_count, connection_duration); 435 | 436 | // Create multiple concurrent tasks 437 | let mut handles = vec![]; 438 | for id in 0..task_count { 439 | let pool = p.clone(); 440 | let success = success_count.clone(); 441 | let timeout = timeout_count.clone(); 442 | let in_progress_counter = in_progress.clone(); 443 | let max_in_progress_counter = max_in_progress.clone(); 444 | 445 | let handle = tokio::spawn(async move { 446 | // Use timeout to prevent indefinite waiting 447 | match pool.get_timeout(Some(Duration::from_secs(1))).await { 448 | Ok(conn) => { 449 | // Successfully got a connection 450 | success.fetch_add(1, Ordering::SeqCst); 451 | 452 | // Track in-progress connections 453 | let current = in_progress_counter.fetch_add(1, Ordering::SeqCst) + 1; 454 | update_max(current, &max_in_progress_counter); 455 | 456 | println!("Task {} got connection, in-progress: {}", id, current); 457 | 458 | // Simulate some work with the connection 459 | tokio::time::sleep(connection_duration).await; 460 | 461 | // Decrease in-progress counter 462 | let remaining = in_progress_counter.fetch_sub(1, Ordering::SeqCst) - 1; 463 | println!("Task {} completed, in-progress: {}", id, remaining); 464 | 465 | // Connection is automatically returned to the pool when dropped 466 | drop(conn); 467 | } 468 | Err(_) => { 469 | // Timed out waiting for a connection 470 | timeout.fetch_add(1, Ordering::SeqCst); 471 | println!("Task {} timed out waiting for connection", id); 472 | } 473 | } 474 | }); 475 | 476 | handles.push(handle); 477 | 478 | // Small delay to simulate staggered requests 479 | tokio::time::sleep(Duration::from_millis(20)).await; 480 | } 481 | 482 | // Periodically print pool stats while waiting 483 | let p_status = p.clone(); 484 | let in_progress_status = in_progress.clone(); 485 | let status_handle = tokio::spawn(async move { 486 | for _ in 0..20 { 487 | tokio::time::sleep(Duration::from_secs(1)).await; 488 | println!("Pool status: {:?}, In-progress: {}", 489 | p_status.state(), in_progress_status.load(Ordering::SeqCst)); 490 | } 491 | }); 492 | 493 | // Wait for all tasks to complete 494 | for handle in handles { 495 | handle.await.unwrap(); 496 | } 497 | 498 | // Wait for status reporting 499 | let _ = status_handle.await; 500 | 501 | // Print final statistics 502 | println!("Connection pool stats:"); 503 | println!(" Max connections setting: {}", max_connections); 504 | println!(" Total tasks: {}", task_count); 505 | println!(" Successful connections: {}", success_count.load(Ordering::SeqCst)); 506 | println!(" Connection timeouts: {}", timeout_count.load(Ordering::SeqCst)); 507 | println!(" Max concurrent connections: {}", max_in_progress.load(Ordering::SeqCst)); 508 | println!(" Final pool state: {:?}", p.state()); 509 | 510 | // Verify pool didn't exceed limits 511 | assert!(max_in_progress.load(Ordering::SeqCst) <= max_connections as usize); 512 | assert!(p.state().connections <= max_connections); 513 | 514 | // Wait for connections to be fully returned to the pool 515 | tokio::time::sleep(Duration::from_millis(200)).await; 516 | 517 | // Verify all connections are idle now 518 | assert_eq!(p.state().in_use, 0); 519 | } 520 | 521 | 522 | #[tokio::test] 523 | async fn test_concurrent_create_connection_less_for_max_open() { 524 | let p = Pool::new(TestManager {}); 525 | p.set_max_open(10); 526 | for _ in 0..1000{ 527 | let p1 = p.clone(); 528 | tokio::spawn(async move { 529 | loop{ 530 | let result = p1.get().await.unwrap(); 531 | tokio::time::sleep(Duration::from_secs(1)).await; 532 | drop(result); 533 | } 534 | }); 535 | } 536 | for _ in 0..5{ 537 | let state = p.state(); 538 | println!("{}", state); 539 | assert_eq!(state.connections <= state.max_open, true); 540 | assert_eq!(state.in_use <= state.max_open, true); 541 | assert_eq!(state.idle <= state.max_open, true); 542 | tokio::time::sleep(Duration::from_secs(1)).await; 543 | } 544 | } 545 | 546 | #[tokio::test] 547 | async fn test_change_max_open() { 548 | let p = Pool::new(TestManager {}); 549 | p.set_max_open(4); 550 | 551 | let c1 = p.get().await.unwrap(); 552 | let c2 = p.get().await.unwrap(); 553 | let c3 = p.get().await.unwrap(); 554 | let c4 = p.get().await.unwrap(); 555 | 556 | p.set_max_open(2); 557 | 558 | drop(c1); 559 | drop(c2); 560 | drop(c3); 561 | drop(c4); 562 | 563 | println!("{}",p.state()); 564 | println!("len {}",p.idle_send.len()); 565 | } 566 | 567 | #[tokio::test] 568 | async fn test_change_max_open2() { 569 | let p = Pool::new(TestManager {}); 570 | p.set_max_open(4); 571 | 572 | let c1 = p.get().await.unwrap(); 573 | let c2 = p.get().await.unwrap(); 574 | let c3 = p.get().await.unwrap(); 575 | let c4 = p.get().await.unwrap(); 576 | 577 | drop(c1); 578 | drop(c2); 579 | drop(c3); 580 | drop(c4); 581 | 582 | p.set_max_open(2); 583 | 584 | println!("{}",p.state()); 585 | println!("len {}",p.idle_send.len()); 586 | } 587 | 588 | #[tokio::test] 589 | async fn test_tokio_cancel() { 590 | let p = Pool::new(TestManager {}); 591 | p.set_max_open(2); 592 | let p1 = p.clone(); 593 | let task = tokio::spawn(async move { 594 | let c1 = p1.get().await.unwrap(); 595 | let c2 = p1.get().await.unwrap(); 596 | tokio::time::sleep(Duration::from_secs(10)).await; 597 | drop(c1); 598 | drop(c2); 599 | }); 600 | tokio::time::sleep(Duration::from_secs(1)).await; 601 | task.abort(); 602 | tokio::time::sleep(Duration::from_secs(1)).await; 603 | println!("{}", p.state()); 604 | assert_eq!(p.state().in_use, 0); 605 | } 606 | 607 | #[tokio::test] 608 | async fn test_tokio_panic() { 609 | let p = Pool::new(TestManager {}); 610 | p.set_max_open(2); 611 | let p1 = p.clone(); 612 | let _task = tokio::spawn(async move { 613 | let _c1 = p1.get().await.unwrap(); 614 | let _c2 = p1.get().await.unwrap(); 615 | panic!("test_tokio_panic"); 616 | }); 617 | tokio::time::sleep(Duration::from_secs(3)).await; 618 | println!("{}", p.state()); 619 | assert_eq!(p.state().in_use, 0); 620 | } 621 | 622 | #[tokio::test] 623 | async fn test_timeout_zero() { 624 | let p = Pool::new(TestManager {}); 625 | p.set_max_open(1); 626 | p.set_timeout_check(None); 627 | let v = p.get().await.unwrap(); 628 | println!("{:?}",v.inner); 629 | } 630 | 631 | #[tokio::test] 632 | async fn test_pool_drop() { 633 | let p = Pool::new(TestManager {}); 634 | p.set_max_open(1); 635 | let v = p.get().await.unwrap(); 636 | println!("{:?}",v.inner); 637 | drop(v); 638 | drop(p); 639 | } --------------------------------------------------------------------------------