1use crate::errors::{KodeBridgeError, Result};
2use crate::transport::{Endpoint, IpcStream};
3use parking_lot::Mutex;
4use std::collections::VecDeque;
5use std::sync::Arc;
6use std::time::{Duration, Instant};
7use tokio::sync::{OwnedSemaphorePermit, Semaphore};
8use tracing::{debug, trace, warn};
9
10#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
12pub struct PoolConfig {
13 pub max_size: usize,
15 pub min_idle: usize,
17 pub max_idle_time_ms: u64,
19 pub connection_timeout_ms: u64,
21 pub retry_delay_ms: u64,
23 pub max_retries: usize,
25 pub max_concurrent_requests: usize,
27 pub max_requests_per_second: Option<f64>,
29}
30
31impl Default for PoolConfig {
32 fn default() -> Self {
33 Self {
34 max_size: 64, min_idle: 8, max_idle_time_ms: 120_000, connection_timeout_ms: 3_000, retry_delay_ms: 10, max_retries: 2, max_concurrent_requests: 32,
41 max_requests_per_second: None,
42 }
43 }
44}
45
46impl PoolConfig {
47 pub const fn max_idle_time(&self) -> Duration {
49 Duration::from_millis(self.max_idle_time_ms)
50 }
51
52 pub const fn connection_timeout(&self) -> Duration {
54 Duration::from_millis(self.connection_timeout_ms)
55 }
56
57 pub const fn retry_delay(&self) -> Duration {
59 Duration::from_millis(self.retry_delay_ms)
60 }
61}
62
63pub struct PooledConnection {
65 inner: Option<IpcStream>,
66 permit: Option<OwnedSemaphorePermit>,
67 created_at: Instant,
68 last_used: Instant,
69 reusable: bool,
70 pool: Arc<ConnectionPoolInner>,
71}
72
73impl PooledConnection {
74 fn new(stream: IpcStream, permit: OwnedSemaphorePermit, pool: Arc<ConnectionPoolInner>) -> Self {
75 let now = Instant::now();
76 Self {
77 inner: Some(stream),
78 permit: Some(permit),
79 created_at: now,
80 last_used: now,
81 reusable: true,
82 pool,
83 }
84 }
85
86 pub fn stream(&mut self) -> Option<&mut IpcStream> {
88 self.last_used = Instant::now();
89 self.inner.as_mut()
90 }
91
92 pub fn into_stream(mut self) -> Option<IpcStream> {
94 self.reusable = false;
95 if let Some(permit) = self.permit.take() {
96 self.pool
97 .active_connections
98 .fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
99 drop(permit);
100 }
101 self.inner.take()
102 }
103
104 pub const fn invalidate(&mut self) {
106 self.reusable = false;
107 }
108
109 pub fn is_valid(&self) -> bool {
111 self.inner.is_some() && self.last_used.elapsed() < self.pool.config.max_idle_time()
112 }
113
114 pub fn age(&self) -> Duration {
116 self.created_at.elapsed()
117 }
118
119 pub fn idle_time(&self) -> Duration {
121 self.last_used.elapsed()
122 }
123}
124
125impl Drop for PooledConnection {
126 fn drop(&mut self) {
127 if let Some(stream) = self.inner.take() {
128 if let Some(permit) = self.permit.take() {
129 self.pool.return_connection(stream, permit, self.reusable);
130 }
131 }
132 }
133}
134
135struct IdleConnection {
136 stream: IpcStream,
137 last_used: Instant,
138 permit: OwnedSemaphorePermit,
139}
140
141struct ConnectionPoolInner {
143 endpoint: Endpoint,
144 config: PoolConfig,
145 connections: Mutex<VecDeque<IdleConnection>>,
146 semaphore: Arc<Semaphore>,
147 active_connections: std::sync::atomic::AtomicUsize,
149}
150
151impl ConnectionPoolInner {
152 fn new(endpoint: Endpoint, config: PoolConfig) -> Self {
153 Self {
154 endpoint,
155 semaphore: Arc::new(Semaphore::new(config.max_size)),
156 connections: Mutex::new(VecDeque::new()),
157 active_connections: std::sync::atomic::AtomicUsize::new(0),
158 config,
159 }
160 }
161
162 async fn get_fresh_connection(&self) -> Result<IpcStream> {
164 let mut last_error = None;
165 for attempt in 0..2 {
166 if attempt > 0 {
167 tokio::time::sleep(Duration::from_millis(10)).await;
168 }
169
170 match IpcStream::connect(&self.endpoint).await {
171 Ok(stream) => {
172 debug!("Created fresh connection for PUT request");
173 return Ok(stream);
174 }
175 Err(e) => {
176 warn!("Fresh connection attempt {} failed: {}", attempt + 1, e);
177 last_error = Some(e);
178 }
179 }
180 }
181
182 Err(KodeBridgeError::connection(format!(
183 "Failed to create fresh connection: {}",
184 last_error
185 .map(|e| e.to_string())
186 .unwrap_or_else(|| "Unknown error".to_string())
187 )))
188 }
189
190 async fn preheat_fresh_connections(&self, count: usize) {
192 let mut successful = 0;
193 for _ in 0..count {
194 let Ok(permit) = Arc::clone(&self.semaphore).try_acquire_owned() else {
195 break;
196 };
197
198 match IpcStream::connect(&self.endpoint).await {
199 Ok(stream) => {
200 self.connections.lock().push_back(IdleConnection {
201 stream,
202 last_used: Instant::now(),
203 permit,
204 });
205 successful += 1;
206 }
207 Err(_) => {
208 drop(permit);
209 break;
210 }
211 }
212 }
213 if successful > 0 {
214 debug!("Preheated {} fresh connections", successful);
215 }
216 }
217
218 async fn create_connection(&self) -> Result<IpcStream> {
219 let mut last_error = None;
220 let mut delay = self.config.retry_delay();
221 let max_delay = Duration::from_millis(200); for attempt in 0..self.config.max_retries {
224 if attempt > 0 {
225 tokio::time::sleep(delay).await;
227 delay = std::cmp::min(delay * 2, max_delay);
228 }
229
230 match IpcStream::connect(&self.endpoint).await {
231 Ok(stream) => {
232 debug!("Created new connection on attempt {}", attempt + 1);
233 return Ok(stream);
234 }
235 Err(e) => {
236 warn!("Connection attempt {} failed: {}", attempt + 1, e);
237 last_error = Some(e);
238 }
239 }
240 }
241
242 Err(KodeBridgeError::connection(format!(
243 "Failed to get fresh connection and no pooled connections available: {}",
244 last_error
245 .map(|e| e.to_string())
246 .unwrap_or_else(|| "Unknown error".to_string())
247 )))
248 }
249
250 fn get_pooled_connection(&self) -> Option<(IpcStream, OwnedSemaphorePermit)> {
251 let mut connections = self.connections.lock();
252
253 let now = Instant::now();
254 while let Some(idle) = connections.front() {
255 if now.duration_since(idle.last_used) > self.config.max_idle_time() {
256 connections.pop_front();
257 } else {
258 break;
259 }
260 }
261
262 connections.pop_front().map(|idle| {
263 trace!("Reusing pooled connection, {} remaining", connections.len());
264 (idle.stream, idle.permit)
265 })
266 }
267
268 fn return_connection(&self, stream: IpcStream, permit: OwnedSemaphorePermit, reusable: bool) {
269 self.active_connections
270 .fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
271
272 if !reusable {
273 trace!("Dropping broken pooled connection");
274 return;
275 }
276
277 let (kept, pool_size) = {
278 let mut connections = self.connections.lock();
279
280 if connections.len() < self.config.max_size {
281 connections.push_back(IdleConnection {
282 stream,
283 last_used: Instant::now(),
284 permit,
285 });
286 (true, connections.len())
287 } else {
288 (false, connections.len())
289 }
290 };
291
292 if kept {
293 trace!("Returned connection to pool, {} total", pool_size);
294 } else {
295 trace!("Pool full, dropping connection");
296 }
297 }
298}
299
300#[derive(Clone)]
302pub struct ConnectionPool {
303 inner: Arc<ConnectionPoolInner>,
304}
305
306impl ConnectionPool {
307 pub fn new(endpoint: Endpoint, config: PoolConfig) -> Self {
309 Self {
310 inner: Arc::new(ConnectionPoolInner::new(endpoint, config)),
311 }
312 }
313
314 pub fn with_default_config(endpoint: Endpoint) -> Self {
316 Self::new(endpoint, PoolConfig::default())
317 }
318
319 pub async fn get_connection(&self) -> Result<PooledConnection> {
321 if let Some((stream, permit)) = self.inner.get_pooled_connection() {
322 self.inner
323 .active_connections
324 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
325 return Ok(PooledConnection::new(stream, permit, Arc::clone(&self.inner)));
326 }
327
328 let timeout = self.inner.config.connection_timeout();
329 let permit = tokio::time::timeout(timeout, Arc::clone(&self.inner.semaphore).acquire_owned())
330 .await
331 .map_err(|_| KodeBridgeError::timeout(timeout.as_millis() as u64))?
332 .map_err(|_| KodeBridgeError::custom("Semaphore closed"))?;
333
334 if let Some((stream, pooled_permit)) = self.inner.get_pooled_connection() {
335 drop(permit);
336 self.inner
337 .active_connections
338 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
339 return Ok(PooledConnection::new(stream, pooled_permit, Arc::clone(&self.inner)));
340 }
341
342 match self.inner.create_connection().await {
343 Ok(stream) => {
344 self.inner
345 .active_connections
346 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
347 Ok(PooledConnection::new(stream, permit, Arc::clone(&self.inner)))
348 }
349 Err(e) => Err(e),
350 }
351 }
352
353 pub async fn get_fresh_connection(&self) -> Result<PooledConnection> {
355 let permit = tokio::time::timeout(
356 Duration::from_millis(100),
357 Arc::clone(&self.inner.semaphore).acquire_owned(),
358 )
359 .await
360 .map_err(|_| KodeBridgeError::timeout(100))?
361 .map_err(|_| KodeBridgeError::custom("Semaphore closed"))?;
362
363 match self.inner.get_fresh_connection().await {
364 Ok(stream) => {
365 self.inner
366 .active_connections
367 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
368 Ok(PooledConnection::new(stream, permit, Arc::clone(&self.inner)))
369 }
370 Err(e) => Err(e),
371 }
372 }
373
374 pub async fn preheat_for_puts(&self, count: usize) {
376 self.inner.preheat_fresh_connections(count).await;
377 }
378
379 pub async fn get_connections(&self, count: usize) -> Result<Vec<PooledConnection>> {
381 let mut connections = Vec::with_capacity(count);
382
383 let mut tasks = Vec::new();
385 for _ in 0..count {
386 let pool = self.clone();
387 tasks.push(tokio::spawn(async move { pool.get_connection().await }));
388 }
389
390 for task in tasks {
392 match task.await {
393 Ok(Ok(conn)) => connections.push(conn),
394 Ok(Err(e)) => return Err(e),
395 Err(e) => return Err(KodeBridgeError::custom(format!("Task failed: {}", e))),
396 }
397 }
398
399 Ok(connections)
400 }
401
402 pub fn stats(&self) -> PoolStats {
404 let connections = self.inner.connections.lock();
405 let active_count = self
406 .inner
407 .active_connections
408 .load(std::sync::atomic::Ordering::Relaxed);
409 PoolStats {
410 total_connections: connections.len() + active_count,
411 available_permits: self.inner.semaphore.available_permits(),
412 max_size: self.inner.config.max_size,
413 active_connections: active_count,
414 }
415 }
416
417 pub fn close(&self) {
419 self.inner.connections.lock().clear();
420 debug!("Closed all pooled connections");
421 }
422}
423
424#[derive(Debug, Clone)]
426pub struct PoolStats {
427 pub total_connections: usize,
428 pub available_permits: usize,
429 pub max_size: usize,
430 pub active_connections: usize,
431}
432
433impl std::fmt::Display for PoolStats {
434 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
435 write!(
436 f,
437 "Pool(connections: {}, active: {}, permits: {}, max: {})",
438 self.total_connections, self.active_connections, self.available_permits, self.max_size
439 )
440 }
441}