Skip to main content

warmup/
warmup.rs

1//! A guided tour of the pool's **lifecycle knobs** — [`min_idle`], [`warm`],
2//! [`state`] — on a single compio runtime, with no sockets and no threads at all.
3//!
4//! There is no server here and no [`std::thread::spawn`]: just one [`Runtime`]
5//! driving one [`LocalPool`], printing a [`State`] snapshot after each step so you
6//! can watch the open/idle counts move. The pooled "connection" is an abstract
7//! resource with an id — the pool is just as useful for any bounded, reused thing
8//! as it is for sockets — and a counter records how many were ever opened, which
9//! is what [`warm`] and reuse are there to keep down.
10//!
11//! What it demonstrates, in order:
12//!
13//!  * [`warm`] opens [`min_idle`] connections up front and parks them idle;
14//!  * [`get`] on a warm pool reuses an idle connection — no new open;
15//!  * dropping a lease returns the connection to the idle list;
16//!  * a burst reuses the idle ones first, then opens more only up to
17//!    [`max_size`], which is the ceiling.
18//!
19//! ```text
20//! cargo run --release --example warmup
21//! ```
22//!
23//! [`min_idle`]: compio_pool::Builder::min_idle
24//! [`max_size`]: compio_pool::Builder::max_size
25//! [`warm`]: compio_pool::LocalPool::warm
26//! [`get`]: compio_pool::LocalPool::get
27//! [`state`]: compio_pool::LocalPool::state
28//! [`State`]: compio_pool::State
29
30use std::{
31    io,
32    sync::atomic::{AtomicU64, Ordering},
33};
34
35use compio::runtime::Runtime;
36use compio_pool::{ManageConnection, Pool};
37
38/// How many resources the manager has ever opened — the number `warm` and reuse
39/// exist to hold down.
40static OPENS: AtomicU64 = AtomicU64::new(0);
41
42/// One pooled resource. A real manager would open a socket or a client here; this
43/// one just stamps an id so `connect` does something observable.
44struct Resource {
45    #[allow(dead_code)]
46    id: u64,
47}
48
49/// Opens abstract resources and counts every open.
50struct Manager;
51
52impl ManageConnection for Manager {
53    type Connection = Resource;
54    type Error = io::Error;
55
56    async fn connect(&self) -> io::Result<Resource> {
57        let id = OPENS.fetch_add(1, Ordering::Relaxed);
58        Ok(Resource { id })
59    }
60
61    async fn is_valid(&self, _conn: &mut Resource) -> io::Result<()> {
62        Ok(())
63    }
64
65    fn has_broken(&self, _conn: &mut Resource) -> bool {
66        false
67    }
68}
69
70fn opened() -> u64 {
71    OPENS.load(Ordering::Relaxed)
72}
73
74fn main() -> io::Result<()> {
75    // Keep at most 8 per thread; warm 4 of them ahead of demand.
76    let pool = Pool::builder().max_size(8).min_idle(4).build(Manager);
77
78    // One runtime, one thread — the pool's natural unit.
79    let runtime = Runtime::builder().build().expect("build compio runtime");
80    runtime.block_on(async move {
81        let local = pool.local();
82        println!("fresh pool:     {:?}  opened={}", local.state(), opened());
83
84        // Warm opens min_idle connections up front and parks them on the idle list.
85        let made = local.warm().await.expect("warm");
86        println!(
87            "after warm:     {:?}  opened={}  (warm made {made})",
88            local.state(),
89            opened()
90        );
91
92        // Checking three out pulls them off the warm idle list — no new opens.
93        let a = local.get().await.expect("get a");
94        let b = local.get().await.expect("get b");
95        let c = local.get().await.expect("get c");
96        println!("3 checked out:  {:?}  opened={}", local.state(), opened());
97
98        // Dropping the leases returns the resources to the idle list.
99        drop((a, b, c));
100        println!("3 returned:     {:?}  opened={}", local.state(), opened());
101
102        // A burst reuses the 4 idle resources first, then opens more — but only up
103        // to max_size=8, which caps the pool. A 9th get here would wait for a free
104        // slot rather than open a ninth.
105        let mut held = Vec::new();
106        for _ in 0..8 {
107            held.push(local.get().await.expect("burst"));
108        }
109        println!("8 checked out:  {:?}  opened={}", local.state(), opened());
110
111        drop(held);
112        println!("8 returned:     {:?}  opened={}", local.state(), opened());
113    });
114
115    Ok(())
116}