pub struct LocalPool<M: ManageConnection> { /* private fields */ }Expand description
One thread’s pool, created by Pool::local.
Holds the idle connections and the slot count for this thread alone. Clone
shares the same per-thread pool between tasks on the thread; it is never
Send.
Implementations§
Source§impl<M: ManageConnection> LocalPool<M>
impl<M: ManageConnection> LocalPool<M>
Sourcepub fn state(&self) -> State
pub fn state(&self) -> State
A snapshot of this thread’s open, idle and waiting counts. Lock-free.
For the process-wide sum use Pool::state.
Examples found in repository?
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}More examples
82fn main() -> io::Result<()> {
83 let requests: usize = arg(1).unwrap_or(50_000);
84 let capacity: u32 = arg(2).unwrap_or(8);
85 let clients: usize = arg(3).unwrap_or(128);
86 let size: usize = arg(4).unwrap_or(256);
87
88 let runtime = Runtime::builder().build().expect("build compio runtime");
89 runtime.block_on(async move {
90 // 1. An in-process echo backend on an ephemeral port, run as a task.
91 let listen: SocketAddr = "127.0.0.1:0".parse().unwrap();
92 let backend = TcpListener::bind(listen).await?;
93 let backend_addr = backend.local_addr()?;
94 compio::runtime::spawn(backend_loop(backend)).detach();
95
96 // 2. One pool of upstreams to the backend, and this thread's LocalPool.
97 let pool = Pool::builder()
98 .max_size(capacity)
99 .build(Upstreams { addr: backend_addr });
100 let local = pool.local();
101
102 println!(
103 "fanout_requests -> {backend_addr}: {clients} client tasks over {capacity} pooled \
104 upstreams, {requests} requests x {size} B"
105 );
106
107 // Shared, single-thread state: plain Rc<Cell>, no atomics needed.
108 let next = Rc::new(Cell::new(0usize)); // next request index to claim
109 let bytes = Rc::new(Cell::new(0u64)); // total bytes echoed back
110 let inflight = Rc::new(Cell::new(0u32)); // upstreams in use right now
111 let peak = Rc::new(Cell::new(0u32)); // high-water mark of `inflight`
112
113 // 3. Fan out: spawn the client tasks and await them all.
114 let started = Instant::now();
115 let mut tasks = Vec::with_capacity(clients);
116 for _ in 0..clients {
117 tasks.push(compio::runtime::spawn(client(
118 local.clone(),
119 next.clone(),
120 requests,
121 size,
122 bytes.clone(),
123 inflight.clone(),
124 peak.clone(),
125 )));
126 }
127 for task in tasks {
128 let _ = task.await;
129 }
130 let elapsed = started.elapsed();
131
132 println!(
133 "done: {requests} requests in {:.3}s ({:.0} req/s), {} B echoed",
134 elapsed.as_secs_f64(),
135 requests as f64 / elapsed.as_secs_f64(),
136 bytes.get(),
137 );
138 println!(
139 "pool: opened {} upstreams total, peak {} in use at once (cap {capacity}); final {:?}",
140 OPENS.load(Ordering::Relaxed),
141 peak.get(),
142 local.state(),
143 );
144 Ok(())
145 })
146}Sourcepub fn statistics(&self) -> Statistics
pub fn statistics(&self) -> Statistics
This thread’s cumulative counters. Lock-free. For the process-wide sum
use Pool::statistics.
Sourcepub async fn warm(&self) -> Result<u32, RunError<M::Error>>
pub async fn warm(&self) -> Result<u32, RunError<M::Error>>
Open connections up to min_idle and park them on the
idle list. Returns how many were created. Tops up to the floor each time it
is called, so it doubles as the warm-up and the min-idle maintenance step;
maintain pairs it with a reap. A no-op (returns Ok(0))
once the pool is closed. Stops early and returns
RunError::User if the manager fails to connect.
Examples found in repository?
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}Sourcepub fn reap(&self) -> u32
pub fn reap(&self) -> u32
Drop idle connections that have passed idle_timeout
or max_lifetime, freeing their slots. Returns how
many were reaped.
Expiry is otherwise lazy — a connection is only checked as it is handed out
or returned — so a pool that goes quiet would hold dead backend connections
open indefinitely. Call this on a timer (see maintain)
to retire them promptly. Only idle connections are considered; one that is
checked out is left to its holder.
Sourcepub async fn maintain(&self) -> Result<u32, RunError<M::Error>>
pub async fn maintain(&self) -> Result<u32, RunError<M::Error>>
The periodic maintenance step: reap expired idle
connections, then warm back up to
min_idle. Returns how many fresh connections were
opened. Call it from a timer on each worker thread — thread-per-core has no
shared reaper thread, so maintenance is yours to drive:
use std::time::Duration;
compio::runtime::spawn(async move {
loop {
compio::time::sleep(Duration::from_secs(30)).await;
let _ = local.maintain().await;
}
})
.detach();Sourcepub fn clear(&self) -> u32
pub fn clear(&self) -> u32
Drop every idle connection now, freeing their slots, and return how many
were dropped. Connections currently checked out are untouched and return as
usual. Use it to drain after a backend failover, or as part of a shutdown
once Pool::close has been called. Unlike reap it ignores
the timeouts and clears unconditionally.
Sourcepub async fn get(&self) -> Result<PooledConnection<M>, RunError<M::Error>>
pub async fn get(&self) -> Result<PooledConnection<M>, RunError<M::Error>>
Check a connection out of this thread’s pool, waiting up to
connection_timeout for one.
Reuses an idle connection when there is one (validated first if
test_on_check_out is set), otherwise opens
one while under max_size, otherwise waits for a slot. The returned
PooledConnection derefs to the connection and returns it to this pool
when dropped. Fails with RunError::Closed if the pool has been
closed.
Examples found in repository?
221async fn query_shard(pool: LocalPool<Shard>, query: String) -> Option<String> {
222 let mut conn = match pool.get().await {
223 Ok(conn) => conn,
224 Err(_) => return None,
225 };
226 if conn.write_line(&query).await.is_err() {
227 conn.healthy = false;
228 return None;
229 }
230 match conn.read_line().await {
231 Ok(Some(reply)) => Some(reply),
232 _ => {
233 conn.healthy = false;
234 None
235 }
236 }
237}More examples
112async fn serve(local: LocalPool<Buffers>, mut stream: TcpStream) {
113 let mut lease = match local.get().await {
114 Ok(lease) => lease,
115 Err(_) => return,
116 };
117 loop {
118 let mut buf = std::mem::take(&mut *lease);
119 buf.clear();
120 let BufResult(read, buf) = stream.read(buf).await;
121 match read {
122 Ok(0) | Err(_) => {
123 *lease = buf;
124 return;
125 }
126 Ok(_) => {}
127 }
128 let BufResult(written, buf) = stream.write_all(buf).await;
129 *lease = buf;
130 if written.is_err() {
131 return;
132 }
133 }
134}91async fn serve(local: LocalPool<Buffers>, mut stream: TcpStream) {
92 let mut lease = match local.get().await {
93 Ok(lease) => lease,
94 Err(_) => return,
95 };
96 loop {
97 let mut buf = std::mem::take(&mut *lease);
98 buf.clear();
99 let BufResult(read, buf) = stream.read(buf).await;
100 match read {
101 Ok(0) | Err(_) => {
102 *lease = buf;
103 return;
104 }
105 Ok(_) => {}
106 }
107 let BufResult(written, buf) = stream.write_all(buf).await;
108 *lease = buf;
109 if written.is_err() {
110 return;
111 }
112 }
113}89async fn serve(local: LocalPool<Buffers>, mut stream: UnixStream) {
90 let mut lease = match local.get().await {
91 Ok(lease) => lease,
92 Err(_) => return,
93 };
94 loop {
95 let mut buf = std::mem::take(&mut *lease);
96 buf.clear();
97 let BufResult(read, buf) = stream.read(buf).await;
98 match read {
99 Ok(0) | Err(_) => {
100 *lease = buf;
101 return;
102 }
103 Ok(_) => {}
104 }
105 let BufResult(written, buf) = stream.write_all(buf).await;
106 *lease = buf;
107 if written.is_err() {
108 return;
109 }
110 }
111}155async fn serve(local: LocalPool<WalManager>, mut stream: TcpStream) {
156 let mut lease = match local.get().await {
157 Ok(lease) => lease,
158 Err(_) => return,
159 };
160 let mut buf = Vec::with_capacity(64 * 1024);
161 loop {
162 buf.clear();
163 let BufResult(read, b) = stream.read(buf).await;
164 buf = b;
165 let n = match read {
166 Ok(0) | Err(_) => return,
167 Ok(n) => n,
168 };
169
170 // Append the record at the segment tail. Read `offset` into a local so the
171 // expression does not borrow `lease` twice.
172 let offset = lease.offset;
173 let BufResult(w, b) = lease.file.write_all_at(buf, offset).await;
174 buf = b;
175 if w.is_err() {
176 return;
177 }
178 lease.offset += n as u64;
179
180 if *SYNC && lease.file.sync_data().await.is_err() {
181 return;
182 }
183
184 let BufResult(w, _) = stream.write_all(b"OK\n".to_vec()).await;
185 if w.is_err() {
186 return;
187 }
188 }
189}149async fn serve(local: LocalPool<Backend>, mut client: TcpStream) {
150 let mut buf = Vec::with_capacity(16 * 1024);
151 loop {
152 buf.clear();
153 let BufResult(read, b) = client.read(buf).await;
154 buf = b;
155 let n = match read {
156 Ok(0) | Err(_) => return,
157 Ok(n) => n,
158 };
159
160 // Lease one upstream for just this round trip.
161 let mut up = match local.get().await {
162 Ok(up) => up,
163 Err(_) => return, // pool exhausted past the timeout, or connect failed
164 };
165
166 // Request out to the upstream.
167 let BufResult(written, b) = up.stream.write_all(buf).await;
168 buf = b;
169 if written.is_err() {
170 up.healthy = false;
171 return;
172 }
173
174 // Echo backend replies with exactly what it got, so read n back.
175 let BufResult(got, reply) = up.stream.read_exact(Vec::with_capacity(n)).await;
176 if got.is_err() {
177 up.healthy = false;
178 return;
179 }
180 drop(up); // return the upstream to the pool before touching the client
181
182 let BufResult(written, _) = client.write_all(reply).await;
183 if written.is_err() {
184 return;
185 }
186 }
187}Trait Implementations§
Source§impl<M: ManageConnection> Clone for LocalPool<M>
impl<M: ManageConnection> Clone for LocalPool<M>
Auto Trait Implementations§
impl<M> !RefUnwindSafe for LocalPool<M>
impl<M> !Send for LocalPool<M>
impl<M> !Sync for LocalPool<M>
impl<M> !UnwindSafe for LocalPool<M>
impl<M> Freeze for LocalPool<M>
impl<M> Unpin for LocalPool<M>
impl<M> UnsafeUnpin for LocalPool<M>where
Rc<Inner<M>>: UnsafeUnpin,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more