do_memory_storage_turso/pool/
mod.rs1pub mod adaptive;
29pub mod caching_pool;
30mod config;
31pub mod connection_wrapper;
32pub mod keepalive;
33#[cfg(test)]
34mod tests;
35
36pub use adaptive::{
37 AdaptiveConnectionPool, AdaptivePoolConfig, AdaptivePoolMetrics, AdaptivePooledConnection,
38 ConnectionCleanupCallback, ConnectionId,
39};
40pub use caching_pool::{CachingPool, CachingPoolConfig, CachingPoolStats, ConnectionGuard};
41pub use config::{PoolConfig, PoolMetrics, PoolMetricsSnapshot, PoolStatistics, PooledConnection};
42pub use connection_wrapper::PooledConnection as WrappedPooledConnection;
43pub use keepalive::{KeepAliveConfig, KeepAliveConnection, KeepAlivePool, KeepAliveStatistics};
44
45use do_memory_core::{Error, Result};
46use libsql::{Connection, Database};
47use parking_lot::RwLock;
48use std::sync::Arc;
49use std::time::{Duration, Instant};
50use tokio::sync::Semaphore;
51use tracing::{debug, info, warn};
52
53pub struct ConnectionPool {
61 db: Arc<Database>,
62 config: PoolConfig,
63 semaphore: Arc<Semaphore>,
64 stats: Arc<RwLock<PoolStatistics>>,
65 pub(crate) metrics: Arc<PoolMetrics>,
67}
68
69impl ConnectionPool {
70 pub async fn new(db: Arc<Database>, config: PoolConfig) -> Result<Self> {
92 info!(
93 "Creating connection pool with min={}, max={}",
94 config.min_connections, config.max_connections
95 );
96
97 let semaphore = Arc::new(Semaphore::new(config.max_connections));
99 let stats = Arc::new(RwLock::new(PoolStatistics::default()));
100 let metrics = Arc::new(PoolMetrics::default());
101
102 let pool = Self {
103 db,
104 config,
105 semaphore,
106 stats,
107 metrics,
108 };
109
110 pool.validate_database().await?;
112
113 info!("Connection pool created successfully");
114 Ok(pool)
115 }
116
117 async fn validate_database(&self) -> Result<()> {
119 let conn = self
120 .db
121 .connect()
122 .map_err(|e| Error::Storage(format!("Failed to connect to database: {}", e)))?;
123
124 conn.query("SELECT 1", ())
125 .await
126 .map_err(|e| Error::Storage(format!("Database validation failed: {}", e)))?;
127
128 Ok(())
129 }
130
131 async fn create_connection(&self) -> Result<Connection> {
133 let conn = self
134 .db
135 .connect()
136 .map_err(|e| Error::Storage(format!("Failed to create connection: {}", e)))?;
137
138 {
140 let mut stats = self.stats.write();
141 stats.total_created += 1;
142 }
143
144 Ok(conn)
145 }
146
147 pub async fn get(&self) -> Result<PooledConnection> {
162 let start = Instant::now();
163
164 let owned_permit_fut = Arc::clone(&self.semaphore).acquire_owned();
166 let permit = tokio::time::timeout(self.config.connection_timeout, owned_permit_fut)
167 .await
168 .map_err(|_| {
169 Error::Storage(format!(
170 "Connection pool timeout after {:?}: max {} connections in use",
171 self.config.connection_timeout, self.config.max_connections
172 ))
173 })?
174 .map_err(|e| Error::Storage(format!("Failed to acquire connection permit: {}", e)))?;
175
176 let wait_time = start.elapsed();
177
178 let conn = self.create_connection().await?;
180
181 if self.config.enable_health_check {
183 if let Err(e) = self.validate_connection_health(&conn).await {
184 let mut stats = self.stats.write();
185 stats.total_health_checks_failed += 1;
186 return Err(e);
187 }
188
189 let mut stats = self.stats.write();
190 stats.total_health_checks_passed += 1;
191 }
192
193 {
195 let mut stats = self.stats.write();
196 stats.total_checkouts += 1;
197 stats.total_wait_time_ms += wait_time.as_millis() as u64;
198 stats.active_connections += 1;
199 stats.update_averages();
200 }
201
202 debug!(
203 "Connection acquired (wait: {:?}, active: {})",
204 wait_time,
205 self.stats.read().active_connections
206 );
207
208 self.metrics.record_acquire(wait_time.as_millis() as u64);
210
211 Ok(PooledConnection {
212 connection: Some(conn),
213 _permit: permit,
214 stats: Arc::clone(&self.stats),
215 metrics: Some(Arc::clone(&self.metrics)),
216 })
217 }
218
219 async fn validate_connection_health(&self, conn: &Connection) -> Result<()> {
221 tokio::time::timeout(self.config.health_check_timeout, conn.query("SELECT 1", ()))
222 .await
223 .map_err(|_| Error::Storage("Connection health check timeout".to_string()))?
224 .map_err(|e| Error::Storage(format!("Connection health check failed: {}", e)))?;
225
226 Ok(())
227 }
228
229 pub async fn statistics(&self) -> PoolStatistics {
231 self.stats.read().clone()
232 }
233
234 pub async fn utilization(&self) -> f32 {
236 let stats = self.stats.read();
237 if self.config.max_connections == 0 {
238 return 0.0;
239 }
240 stats.active_connections as f32 / self.config.max_connections as f32
241 }
242
243 pub async fn available_connections(&self) -> usize {
245 let stats = self.stats.read();
246 self.config
247 .max_connections
248 .saturating_sub(stats.active_connections)
249 }
250
251 pub async fn has_capacity(&self) -> bool {
253 self.available_connections().await > 0
254 }
255
256 pub async fn shutdown(&self) -> Result<()> {
260 info!("Shutting down connection pool");
261
262 let shutdown_timeout = Duration::from_secs(30);
263 let start = Instant::now();
264
265 while start.elapsed() < shutdown_timeout {
266 let active = self.stats.read().active_connections;
267 if active == 0 {
268 break;
269 }
270
271 debug!("Waiting for {} active connections to complete", active);
272 tokio::time::sleep(Duration::from_millis(100)).await;
273 }
274
275 let final_active = self.stats.read().active_connections;
276 if final_active > 0 {
277 warn!(
278 "Shutdown completed with {} active connections still in use",
279 final_active
280 );
281 } else {
282 info!("Connection pool shutdown complete");
283 }
284
285 Ok(())
286 }
287}