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