ignitia 0.2.4

A blazing fast, lightweight web framework for Rust that ignites your development journey
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
//! Object pooling for high-performance request handling
//!
//! This module provides a high-performance object pool implementation designed for
//! web server scenarios where frequent object allocation and deallocation can impact
//! performance. The pool supports configuration for different use cases including
//! high-RPS scenarios, memory-constrained environments, and long-lived connections.
//!
//! # Features
//!
//! - **Generic object pooling**: Pool any type implementing the `Poolable` trait
//! - **Thread-safe**: Full concurrent access support with minimal lock contention
//! - **Configurable**: Tune pool behavior for different scenarios
//! - **Statistics**: Optional performance monitoring and debugging
//! - **Validation**: Optional object validation before reuse
//! - **Timeout handling**: Configurable acquire timeouts
//!
//! # Examples
//!
//! ## Basic Usage
//!
//! ```
//! use crate::server::pool::{ObjectPool, PoolConfig, Poolable};
//! use std::time::Duration;
//!
//! // Create a pool for byte vectors
//! let pool = ObjectPool::new(
//!     PoolConfig::default(),
//!     || Vec::<u8>::with_capacity(8192)
//! );
//!
//! // Acquire an object
//! let mut buffer = pool.acquire()?;
//! buffer.extend_from_slice(b"Hello, world!");
//!
//! // Object is automatically returned to pool when dropped
//! ```
//!
//! ## High-Performance Configuration
//!
//! ```
//! let config = PoolConfig::high_rps();
//! let pool = ObjectPool::new(config, || Vec::<u8>::with_capacity(4096));
//! ```
//!
//! ## Global Object Pools
//!
//! ```
//! use crate::server::pool::ObjectPools;
//!
//! let pools = ObjectPools::new();
//! let buffer = pools.buffer_pool.acquire()?;
//! let string = pools.string_pool.acquire()?;
//! ```

use crate::Result;
use parking_lot::Mutex;
use std::collections::VecDeque;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};

/// Configuration for object pooling behavior
///
/// This struct controls how the object pool behaves in different scenarios.
/// Use the predefined configurations for common use cases, or customize
/// for specific requirements.
#[derive(Debug, Clone)]
pub struct PoolConfig {
    /// Initial number of objects to pre-allocate in the pool
    ///
    /// Higher values reduce allocation overhead during startup but use more memory.
    /// For high-throughput scenarios, consider 100-500 initial objects.
    pub initial_size: usize,

    /// Maximum number of objects the pool can hold
    ///
    /// Objects beyond this limit will be dropped instead of returned to pool.
    /// Set based on available memory and expected concurrent usage.
    pub max_size: usize,

    /// Maximum time to wait when acquiring objects from pool
    ///
    /// If the pool is empty and at max size, acquisition will wait up to this duration
    /// before creating a new object anyway or returning an error.
    pub acquire_timeout: Duration,

    /// Whether to validate objects before reusing them
    ///
    /// When enabled, objects returning from the pool are validated using `Poolable::is_valid()`.
    /// Invalid objects are discarded and new ones created. Disable for maximum performance.
    pub validate_objects: bool,

    /// Whether to collect detailed statistics about pool usage
    ///
    /// Statistics include creation count, reuse count, and timing metrics.
    /// Disable in production for slightly better performance.
    pub enable_stats: bool,
}

impl Default for PoolConfig {
    fn default() -> Self {
        Self {
            initial_size: 100,
            max_size: 1000,
            acquire_timeout: Duration::from_millis(100),
            validate_objects: true,
            enable_stats: true,
        }
    }
}

/// Statistics collected by the object pool
///
/// These metrics help monitor pool performance and tune configuration.
/// Only collected when `PoolConfig::enable_stats` is true.
#[derive(Debug, Default)]
pub struct PoolStats {
    /// Total number of new objects created by the pool
    pub total_created: AtomicUsize,

    /// Total number of objects reused from the pool
    pub total_reused: AtomicUsize,

    /// Current number of objects in the pool
    pub current_size: AtomicUsize,

    /// Maximum pool size reached during operation
    pub max_size_reached: AtomicUsize,

    /// Number of times acquisition timed out
    pub acquire_timeouts: AtomicUsize,

    /// Average time to acquire objects (when stats enabled)
    pub avg_acquire_time: Mutex<Duration>,
}

/// A high-performance, thread-safe object pool
///
/// The object pool manages a collection of reusable objects to reduce allocation overhead.
/// It's particularly useful for frequently allocated objects like buffers, strings, or
/// connection objects in high-throughput scenarios.
///
/// # Thread Safety
///
/// The pool is fully thread-safe and can be shared across multiple threads using `Arc`.
/// Internal synchronization uses `parking_lot::Mutex` for better performance than std mutexes.
///
/// # Memory Management
///
/// - Objects are stored in a `VecDeque` for efficient LIFO access
/// - Pool size is bounded by `max_size` to prevent unbounded growth
/// - Objects exceeding the limit are dropped rather than stored
/// - Statistics track memory usage patterns when enabled
pub struct ObjectPool<T: Poolable> {
    /// Thread-safe storage for pooled objects
    objects: Mutex<VecDeque<T>>,

    /// Pool configuration
    config: PoolConfig,

    /// Performance and usage statistics
    stats: PoolStats,

    /// Factory function for creating new objects
    factory: Arc<dyn Fn() -> T + Send + Sync>,
}

/// Trait for objects that can be pooled
///
/// Objects must implement this trait to be used with `ObjectPool`.
/// The trait ensures objects can be reset to a clean state and validated.
pub trait Poolable: Sized + Send + 'static {
    /// Reset the object to its initial state for reuse
    ///
    /// This method is called before returning an object from the pool.
    /// It should clear any data and reset the object to a clean state.
    ///
    /// # Example
    ///
    /// ```
    /// impl Poolable for Vec<u8> {
    ///     fn reset(&mut self) {
    ///         self.clear(); // Clear data but keep capacity
    ///     }
    /// }
    /// ```
    fn reset(&mut self);

    /// Check if the object is in a valid state for reuse
    ///
    /// This method is called when `PoolConfig::validate_objects` is true.
    /// Return false if the object should be discarded instead of reused.
    ///
    /// # Example
    ///
    /// ```
    /// impl Poolable for Vec<u8> {
    ///     fn is_valid(&self) -> bool {
    ///         self.capacity() <= 1024 * 1024 // Max 1MB capacity
    ///     }
    /// }
    /// ```
    fn is_valid(&self) -> bool;
}

impl<T: Poolable> ObjectPool<T> {
    /// Create a new object pool with the given configuration and factory function
    ///
    /// # Arguments
    ///
    /// * `config` - Pool configuration controlling behavior
    /// * `factory` - Function to create new objects when needed
    ///
    /// # Examples
    ///
    /// ```
    /// let pool = ObjectPool::new(
    ///     PoolConfig::default(),
    ///     || Vec::<u8>::with_capacity(4096)
    /// );
    /// ```
    pub fn new<F>(config: PoolConfig, factory: F) -> Self
    where
        F: Fn() -> T + Send + Sync + 'static,
    {
        let mut pool = Self {
            objects: Mutex::new(VecDeque::with_capacity(config.initial_size)),
            config,
            stats: PoolStats::default(),
            factory: Arc::new(factory),
        };

        // Pre-populate pool
        pool.warmup();
        pool
    }

    /// Pre-populate the pool with initial objects
    ///
    /// This method is called during construction to create the initial set of objects.
    /// It reduces allocation overhead during the initial phase of operation.
    fn warmup(&mut self) {
        let mut objects = self.objects.lock();
        for _ in 0..self.config.initial_size {
            let obj = (self.factory)();
            objects.push_back(obj);
            self.stats.total_created.fetch_add(1, Ordering::Relaxed);
        }
        self.stats
            .current_size
            .store(self.config.initial_size, Ordering::Relaxed);
    }

    /// Acquire an object from the pool
    ///
    /// This method attempts to get an object from the pool, reusing existing objects
    /// when possible. If the pool is empty, it creates new objects up to the maximum
    /// pool size. Beyond that limit, it may wait or create temporary objects.
    ///
    /// # Returns
    ///
    /// Returns a `PooledObject<T>` that automatically returns to the pool when dropped.
    ///
    /// # Errors
    ///
    /// Returns an error if acquisition times out (when pool is at capacity and
    /// `acquire_timeout` is exceeded).
    ///
    /// # Examples
    ///
    /// ```
    /// let pool = ObjectPool::new(PoolConfig::default(), || Vec::<u8>::new());
    /// let mut buffer = pool.acquire()?;
    /// buffer.push(42);
    /// // Buffer automatically returns to pool when dropped
    /// ```
    pub fn acquire(&self) -> Result<PooledObject<T>> {
        let start = Instant::now();

        // Try to get from pool first
        if let Some(mut obj) = self.objects.lock().pop_front() {
            self.stats.current_size.fetch_sub(1, Ordering::Relaxed);
            self.stats.total_reused.fetch_add(1, Ordering::Relaxed);

            // Validate object if enabled
            if self.config.validate_objects && !obj.is_valid() {
                // Create new object if validation fails
                let new_obj = (self.factory)();
                self.stats.total_created.fetch_add(1, Ordering::Relaxed);
                return Ok(PooledObject::new(new_obj, self));
            }

            obj.reset();
            return Ok(PooledObject::new(obj, self));
        }

        // Create new object if pool is empty but under max size
        let current_size = self.stats.current_size.load(Ordering::Relaxed);
        if current_size < self.config.max_size {
            let obj = (self.factory)();
            self.stats.total_created.fetch_add(1, Ordering::Relaxed);
            self.stats.current_size.fetch_add(1, Ordering::Relaxed);
            return Ok(PooledObject::new(obj, self));
        }

        // Pool is at max size, wait or timeout
        if start.elapsed() > self.config.acquire_timeout {
            self.stats.acquire_timeouts.fetch_add(1, Ordering::Relaxed);
            return Err(crate::Error::Internal("Pool acquire timeout".to_string()));
        }

        // Create new object anyway (exceeding max size temporarily)
        let obj = (self.factory)();
        self.stats.total_created.fetch_add(1, Ordering::Relaxed);
        Ok(PooledObject::new(obj, self))
    }

    /// Return an object to the pool
    ///
    /// This method is called automatically when a `PooledObject` is dropped.
    /// It stores the object in the pool for future reuse, unless the pool is at capacity.
    ///
    /// # Arguments
    ///
    /// * `obj` - The object to return to the pool
    fn release(&self, obj: T) {
        let mut objects = self.objects.lock();
        let current_size = objects.len();

        if current_size < self.config.max_size {
            objects.push_back(obj);
            self.stats
                .current_size
                .store(current_size + 1, Ordering::Relaxed);
            self.stats
                .max_size_reached
                .fetch_max(current_size + 1, Ordering::Relaxed);
        }
        // If pool is full, the object will be dropped
    }

    /// Get pool statistics
    ///
    /// Returns a reference to the pool's performance statistics.
    /// Statistics are only meaningful when `PoolConfig::enable_stats` is true.
    ///
    /// # Examples
    ///
    /// ```
    /// let stats = pool.stats();
    /// println!("Created: {}, Reused: {}, Current size: {}",
    ///     stats.total_created.load(Ordering::Relaxed),
    ///     stats.total_reused.load(Ordering::Relaxed),
    ///     stats.current_size.load(Ordering::Relaxed)
    /// );
    /// ```
    pub fn stats(&self) -> &PoolStats {
        &self.stats
    }

    /// Clear all objects from the pool
    ///
    /// This method removes and drops all objects currently in the pool.
    /// It's useful for cleanup or when changing pool configuration.
    pub fn clear(&self) {
        let mut objects = self.objects.lock();
        objects.clear();
        self.stats.current_size.store(0, Ordering::Relaxed);
    }
}

/// A pooled object that returns to the pool when dropped
///
/// This wrapper ensures that objects are automatically returned to their pool
/// when they go out of scope. It implements `Deref` and `DerefMut` to provide
/// transparent access to the underlying object.
///
/// # Automatic Cleanup
///
/// The object is automatically returned to the pool when the `PooledObject`
/// is dropped, ensuring proper resource management without manual intervention.
pub struct PooledObject<'a, T: Poolable> {
    /// The pooled object (None when already returned)
    object: Option<T>,

    /// Reference to the pool for returning the object
    pool: &'a ObjectPool<T>,

    /// Time when the object was acquired (for statistics)
    acquire_time: Instant,
}

impl<'a, T: Poolable> PooledObject<'a, T> {
    /// Create a new pooled object wrapper
    ///
    /// This method is called internally by `ObjectPool::acquire()`.
    fn new(object: T, pool: &'a ObjectPool<T>) -> Self {
        Self {
            object: Some(object),
            pool,
            acquire_time: Instant::now(),
        }
    }
}

impl<'a, T: Poolable> Deref for PooledObject<'a, T> {
    type Target = T;
    /// Provide immutable access to the underlying object
    fn deref(&self) -> &Self::Target {
        self.object.as_ref().unwrap()
    }
}

impl<'a, T: Poolable> DerefMut for PooledObject<'a, T> {
    /// Provide mutable access to the underlying object
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.object.as_mut().unwrap()
    }
}

impl<'a, T: Poolable> Drop for PooledObject<'a, T> {
    /// Automatically return the object to the pool when dropped
    fn drop(&mut self) {
        if let Some(object) = self.object.take() {
            self.pool.release(object);
        }
    }
}

/// Predefined pool configurations for common scenarios
impl PoolConfig {
    /// Configuration optimized for high-RPS scenarios
    ///
    /// This configuration maximizes performance by:
    /// - Large initial and maximum pool sizes
    /// - Short acquire timeout
    /// - Disabled validation and statistics for minimum overhead
    ///
    /// Use this for high-throughput web servers where performance is critical.
    pub fn high_rps() -> Self {
        Self {
            initial_size: 500,
            max_size: 5000,
            acquire_timeout: Duration::from_millis(10),
            validate_objects: false, // Disable validation for maximum speed
            enable_stats: false,     // Disable stats for less overhead
        }
    }

    /// Configuration for memory-constrained environments
    ///
    /// This configuration minimizes memory usage by:
    /// - Small initial and maximum pool sizes
    /// - Enabled validation to ensure objects don't grow too large
    /// - Statistics enabled for monitoring
    ///
    /// Use this in embedded systems or when memory is limited.
    pub fn memory_constrained() -> Self {
        Self {
            initial_size: 50,
            max_size: 200,
            acquire_timeout: Duration::from_millis(50),
            validate_objects: true,
            enable_stats: true,
        }
    }

    /// Configuration for long-lived connections
    ///
    /// This configuration balances performance and resource management:
    /// - Moderate pool sizes
    /// - Longer acquire timeout for stability
    /// - Validation and statistics enabled for monitoring
    ///
    /// Use this for connection pools or other long-lived resources.
    pub fn long_lived() -> Self {
        Self {
            initial_size: 100,
            max_size: 1000,
            acquire_timeout: Duration::from_millis(100),
            validate_objects: true,
            enable_stats: true,
        }
    }
}

/// Poolable implementation for byte vectors
///
/// Byte vectors are commonly used for I/O buffers in web servers.
/// The implementation clears the vector but preserves capacity for efficiency.
impl Poolable for Vec<u8> {
    fn reset(&mut self) {
        self.clear();
    }

    fn is_valid(&self) -> bool {
        self.capacity() <= 1024 * 1024 // 1MB max capacity
    }
}

/// Poolable implementation for strings
///
/// Strings are commonly used for text processing in web applications.
/// The implementation clears the string but preserves capacity for efficiency.
impl Poolable for String {
    fn reset(&mut self) {
        self.clear();
    }

    fn is_valid(&self) -> bool {
        self.capacity() <= 1024 * 1024 // 1MB max capacity
    }
}

/// Global object pools for common types
///
/// This struct provides pre-configured pools for commonly used types
/// in web server scenarios. Use this for convenience when you don't
/// need custom pool configuration.
///
/// # Examples
///
/// ```
/// let pools = ObjectPools::new();
///
/// // Get a buffer for I/O operations
/// let mut buffer = pools.buffer_pool.acquire()?;
/// buffer.extend_from_slice(b"Hello, world!");
///
/// // Get a string for text processing
/// let mut text = pools.string_pool.acquire()?;
/// text.push_str("Processing request...");
/// ```
pub struct ObjectPools {
    /// Pool for byte vectors (I/O buffers)
    pub buffer_pool: Arc<ObjectPool<Vec<u8>>>,
    /// Pool for strings (text processing)
    pub string_pool: Arc<ObjectPool<String>>,
}

impl ObjectPools {
    /// Create global pools with default high-performance configuration
    ///
    /// Uses `PoolConfig::high_rps()` for maximum throughput.
    pub fn new() -> Self {
        Self {
            buffer_pool: Arc::new(ObjectPool::new(PoolConfig::high_rps(), || {
                Vec::with_capacity(8192)
            })),
            string_pool: Arc::new(ObjectPool::new(PoolConfig::high_rps(), || {
                String::with_capacity(1024)
            })),
        }
    }

    /// Create pools with custom configuration
    ///
    /// Use this when you need different pool behavior than the default.
    ///
    /// # Arguments
    ///
    /// * `config` - Configuration to use for both pools
    pub fn with_config(config: PoolConfig) -> Self {
        Self {
            buffer_pool: Arc::new(ObjectPool::new(config.clone(), || Vec::with_capacity(8192))),
            string_pool: Arc::new(ObjectPool::new(config, || String::with_capacity(1024))),
        }
    }
}

impl Default for ObjectPools {
    fn default() -> Self {
        Self::new()
    }
}