1use std::collections::VecDeque;
13use std::sync::Arc;
14use std::time::Duration;
15
16use parking_lot::Mutex;
17use tokio::sync::Semaphore;
18use tokio_rusqlite::Connection;
19use tracing::{debug, info, trace};
20
21use crate::config::SqliteConfig;
22use crate::connection::{PooledConnection, SqliteConnection};
23use crate::error::{SqliteError, SqliteResult};
24
25#[derive(Clone)]
41pub struct SqlitePool {
42 config: Arc<SqliteConfig>,
43 semaphore: Arc<Semaphore>,
45 idle_connections: Arc<Mutex<VecDeque<PooledConnection>>>,
47 pool_config: Arc<PoolConfig>,
48 stats: Arc<Mutex<PoolStats>>,
50}
51
52#[derive(Debug, Default, Clone)]
54pub struct PoolStats {
55 pub reuses: u64,
57 pub opens: u64,
59 pub expirations: u64,
61 pub in_use: usize,
63}
64
65impl SqlitePool {
66 pub async fn new(config: SqliteConfig) -> SqliteResult<Self> {
68 Self::with_pool_config(config, PoolConfig::default()).await
69 }
70
71 pub async fn with_pool_config(
73 config: SqliteConfig,
74 pool_config: PoolConfig,
75 ) -> SqliteResult<Self> {
76 info!(
77 path = %config.path_str(),
78 max_connections = %pool_config.max_connections,
79 "SQLite connection pool created"
80 );
81
82 let test_conn = Self::open_connection(&config).await?;
84 drop(test_conn);
85
86 let pool = Self {
87 config: Arc::new(config),
88 semaphore: Arc::new(Semaphore::new(pool_config.max_connections)),
89 idle_connections: Arc::new(Mutex::new(VecDeque::with_capacity(
90 pool_config.max_connections,
91 ))),
92 pool_config: Arc::new(pool_config),
93 stats: Arc::new(Mutex::new(PoolStats::default())),
94 };
95
96 if !pool.config.path.is_memory() && pool.pool_config.min_connections > 0 {
98 debug!(
99 "Pre-warming pool with {} connections",
100 pool.pool_config.min_connections
101 );
102 for _ in 0..pool.pool_config.min_connections {
103 if let Ok(conn) = Self::open_connection(&pool.config).await {
104 let mut idle = pool.idle_connections.lock();
105 idle.push_back(PooledConnection::new(conn));
106 }
107 }
108 }
109
110 Ok(pool)
111 }
112
113 async fn open_connection(config: &SqliteConfig) -> SqliteResult<Connection> {
115 let path = config.path_str().to_string();
116 let init_sql = config.init_sql();
117
118 let conn = if config.path.is_memory() {
119 Connection::open_in_memory().await?
120 } else {
121 Connection::open(&path).await?
122 };
123
124 conn.call(move |conn| {
126 conn.execute_batch(&init_sql)?;
127 Ok(())
128 })
129 .await?;
130
131 #[cfg(feature = "vector")]
145 {
146 use std::sync::Once;
147 static WARN_ONCE: Once = Once::new();
148
149 let _ = conn
150 .call(|conn| {
151 if let Err(e) = crate::vector::register_vector_extension(conn) {
152 WARN_ONCE.call_once(|| {
153 tracing::warn!(
154 error = %e,
155 "sqlite-vector-rs extension could not be registered; \
156 vector SQL functions will be unavailable on this connection. \
157 Build libsqlite_vector_rs.so and set SQLITE_VECTOR_RS_LIB \
158 or place it alongside the test/binary. \
159 (This warning is emitted once per process.)"
160 );
161 });
162 }
163 Ok(())
164 })
165 .await;
166 }
167
168 Ok(conn)
169 }
170
171 pub async fn get(&self) -> SqliteResult<SqliteConnection> {
177 trace!("Acquiring connection from pool");
178
179 let permit = self
181 .semaphore
182 .clone()
183 .acquire_owned()
184 .await
185 .map_err(|e| SqliteError::pool(format!("failed to acquire permit: {}", e)))?;
186
187 {
189 let mut stats = self.stats.lock();
190 stats.in_use += 1;
191 }
192
193 if self.config.path.is_memory() {
196 let conn = Self::open_connection(&self.config).await?;
197 {
198 let mut stats = self.stats.lock();
199 stats.opens += 1;
200 }
201 return Ok(SqliteConnection::new_pooled(
202 conn, permit, None, ));
204 }
205
206 let conn: Option<Connection> = {
208 let mut idle = self.idle_connections.lock();
209
210 while let Some(pooled) = idle.pop_front() {
212 let is_expired = if let Some(lifetime) = self.pool_config.max_lifetime {
213 pooled.created_at.elapsed() > lifetime
214 } else {
215 false
216 };
217 let is_idle_expired = if let Some(timeout) = self.pool_config.idle_timeout {
218 pooled.last_used.elapsed() > timeout
219 } else {
220 false
221 };
222
223 if is_expired || is_idle_expired {
224 let mut stats = self.stats.lock();
225 stats.expirations += 1;
226 continue;
228 }
229 let mut stats = self.stats.lock();
231 stats.reuses += 1;
232 return Ok(SqliteConnection::new_pooled(
233 pooled.conn,
234 permit,
235 Some(self.idle_connections.clone()),
236 ));
237 }
238 None
239 };
240
241 if conn.is_none() {
243 debug!("No idle connections, opening new connection");
244 let new_conn = Self::open_connection(&self.config).await?;
245 {
246 let mut stats = self.stats.lock();
247 stats.opens += 1;
248 }
249 return Ok(SqliteConnection::new_pooled(
250 new_conn,
251 permit,
252 Some(self.idle_connections.clone()),
253 ));
254 }
255
256 unreachable!()
257 }
258
259 pub fn config(&self) -> &SqliteConfig {
261 &self.config
262 }
263
264 pub fn pool_config(&self) -> &PoolConfig {
266 &self.pool_config
267 }
268
269 pub fn stats(&self) -> PoolStats {
271 self.stats.lock().clone()
272 }
273
274 pub fn reset_stats(&self) {
276 let mut stats = self.stats.lock();
277 *stats = PoolStats::default();
278 }
279
280 pub async fn is_healthy(&self) -> bool {
282 match Self::open_connection(&self.config).await {
283 Ok(conn) => {
284 let result = conn
285 .call(|conn| {
286 conn.execute("SELECT 1", [])?;
287 Ok(())
288 })
289 .await;
290 result.is_ok()
291 }
292 Err(_) => false,
293 }
294 }
295
296 pub fn available_permits(&self) -> usize {
298 self.semaphore.available_permits()
299 }
300
301 pub fn idle_count(&self) -> usize {
303 self.idle_connections.lock().len()
304 }
305
306 pub fn builder() -> SqlitePoolBuilder {
308 SqlitePoolBuilder::new()
309 }
310}
311
312#[derive(Debug, Clone)]
314pub struct PoolConfig {
315 pub max_connections: usize,
317 pub min_connections: usize,
319 pub connection_timeout: Option<Duration>,
321 pub idle_timeout: Option<Duration>,
323 pub max_lifetime: Option<Duration>,
325}
326
327impl Default for PoolConfig {
328 fn default() -> Self {
329 Self {
330 max_connections: 5, min_connections: 1,
332 connection_timeout: Some(Duration::from_secs(30)),
333 idle_timeout: Some(Duration::from_secs(300)), max_lifetime: Some(Duration::from_secs(1800)), }
336 }
337}
338
339#[derive(Debug, Default)]
341pub struct SqlitePoolBuilder {
342 config: Option<SqliteConfig>,
343 url: Option<String>,
344 pool_config: PoolConfig,
345}
346
347impl SqlitePoolBuilder {
348 pub fn new() -> Self {
350 Self {
351 config: None,
352 url: None,
353 pool_config: PoolConfig::default(),
354 }
355 }
356
357 pub fn url(mut self, url: impl Into<String>) -> Self {
359 self.url = Some(url.into());
360 self
361 }
362
363 pub fn config(mut self, config: SqliteConfig) -> Self {
365 self.config = Some(config);
366 self
367 }
368
369 pub fn max_connections(mut self, n: usize) -> Self {
371 self.pool_config.max_connections = n;
372 self
373 }
374
375 pub fn connection_timeout(mut self, timeout: Duration) -> Self {
377 self.pool_config.connection_timeout = Some(timeout);
378 self
379 }
380
381 pub fn idle_timeout(mut self, timeout: Duration) -> Self {
383 self.pool_config.idle_timeout = Some(timeout);
384 self
385 }
386
387 pub async fn build(self) -> SqliteResult<SqlitePool> {
389 let config = if let Some(config) = self.config {
390 config
391 } else if let Some(url) = self.url {
392 SqliteConfig::from_url(url)?
393 } else {
394 return Err(SqliteError::config("no database URL or config provided"));
395 };
396
397 SqlitePool::with_pool_config(config, self.pool_config).await
398 }
399}
400
401#[cfg(test)]
402mod tests {
403 use super::*;
404
405 #[test]
406 fn test_pool_config_default() {
407 let config = PoolConfig::default();
408 assert_eq!(config.max_connections, 5);
409 }
410
411 #[test]
412 fn test_pool_builder() {
413 let builder = SqlitePoolBuilder::new()
414 .url("sqlite::memory:")
415 .max_connections(10);
416
417 assert!(builder.url.is_some());
418 assert_eq!(builder.pool_config.max_connections, 10);
419 }
420
421 #[tokio::test]
422 async fn test_pool_memory() {
423 let pool = SqlitePool::new(SqliteConfig::memory()).await.unwrap();
424 assert!(pool.available_permits() > 0);
427 }
428
429 #[tokio::test]
430 async fn test_pool_get_connection() {
431 let pool = SqlitePool::new(SqliteConfig::memory()).await.unwrap();
432 let conn = pool.get().await;
433 assert!(conn.is_ok());
434 }
435}