pub struct ClientPool<P: Protocol> { /* private fields */ }Expand description
Concurrent pool of linked clients for one [Protocol].
Provides async access to the underlying map via an internal
[tokio::sync::Mutex]. The reverse index (by_module) is kept in
lockstep with the primary map so fan-out can enumerate members of
a module in O(module members) without scanning every client.
Implementations§
Source§impl<P: Protocol> ClientPool<P>
impl<P: Protocol> ClientPool<P>
Sourcepub async fn insert(&self, peer: SocketAddr, handle: ClientHandle<P>)
pub async fn insert(&self, peer: SocketAddr, handle: ClientHandle<P>)
Insert a client handle keyed by peer address.
If the handle already has a module set, the reverse index is updated to include this peer under that module.
§Cancellation safety
This method is not cancel-safe. It takes two internal
mutex locks in sequence: dropping the future between the first
and second lock().await leaves the forward map updated but
the reverse module index stale. The only correct recovery is
to call Self::remove for the same peer and retry.
Sourcepub async fn remove(&self, peer: &SocketAddr) -> Option<ClientHandle<P>>
pub async fn remove(&self, peer: &SocketAddr) -> Option<ClientHandle<P>>
Remove a client by peer address, returning the handle if present.
§Cancellation safety
This method is not cancel-safe for the same reason as
Self::insert — it touches the forward and reverse maps in
sequence and cancellation between the two awaits leaves a
stale module-index entry.
Sourcepub async fn set_module(&self, peer: &SocketAddr, module: Module)
pub async fn set_module(&self, peer: &SocketAddr, module: Module)
Attach a peer to a module, updating both the handle and the reverse index.
No-op if the peer is not in the pool. If the peer was already attached to a different module, the old entry is removed.
§Cancellation safety
This method is not cancel-safe. See Self::insert for
the dual-lock sequencing rationale.
Sourcepub async fn members_of_module(&self, module: Module) -> Vec<SocketAddr>
pub async fn members_of_module(&self, module: Module) -> Vec<SocketAddr>
Enumerate the peers currently linked to the given module.
§Cancellation safety
This method is cancel-safe. It acquires a single mutex lock and clones the membership set; dropping the future either before or after the clone leaves the pool state unchanged.
Sourcepub async fn len(&self) -> usize
pub async fn len(&self) -> usize
Number of clients currently in the pool.
§Cancellation safety
This method is cancel-safe. It takes a single mutex lock and
reads a usize; no mutation occurs.
Sourcepub async fn contains(&self, peer: &SocketAddr) -> bool
pub async fn contains(&self, peer: &SocketAddr) -> bool
Sourcepub async fn mark_unhealthy(&self, peer: &SocketAddr) -> UnhealthyOutcome
pub async fn mark_unhealthy(&self, peer: &SocketAddr) -> UnhealthyOutcome
Increment the send-failure counter on a peer and report whether the peer should now be evicted.
Returns UnhealthyOutcome::StillHealthy with failure_count
0 if the peer is not present — callers should treat that as
“no increment happened” rather than “counter reset”.
The eviction threshold is DEFAULT_UNHEALTHY_THRESHOLD. Once
failure_count >= threshold the return value switches to
UnhealthyOutcome::ShouldEvict and stays there on every
subsequent increment until the caller actually removes the
peer.
§Cancellation safety
This method is cancel-safe. Only a single mutex lock is taken
and the mutation is committed before drop(clients) releases
the lock; dropping the future after that point has no effect
on pool state.
Sourcepub async fn record_last_heard(&self, peer: &SocketAddr, now: Instant)
pub async fn record_last_heard(&self, peer: &SocketAddr, now: Instant)
Record that we just received a datagram from the given peer.
§Cancellation safety
This method is cancel-safe. See Self::mark_unhealthy.
Sourcepub async fn module_of(&self, peer: &SocketAddr) -> Option<Module>
pub async fn module_of(&self, peer: &SocketAddr) -> Option<Module>
Sourcepub async fn access_of(&self, peer: &SocketAddr) -> Option<AccessPolicy>
pub async fn access_of(&self, peer: &SocketAddr) -> Option<AccessPolicy>
Look up the AccessPolicy for a peer, if any.
Returns None if the peer is not in the pool. Used by the
endpoint to gate voice-frame forwarding for AccessPolicy::ReadOnly
clients.
§Cancellation safety
This method is cancel-safe. See Self::len.
Sourcepub async fn try_consume_tx_token(
&self,
peer: &SocketAddr,
now: Instant,
) -> bool
pub async fn try_consume_tx_token( &self, peer: &SocketAddr, now: Instant, ) -> bool
Attempt to consume one TX-budget token from the given peer.
Returns true on success (the fan-out engine may send this
frame to the peer); returns false if the bucket is empty
(the frame must be dropped for this peer). Returns false
if the peer is not in the pool — no handle means no budget.
The now argument is the caller’s injected wall-clock
instant and is used to drive the token bucket’s refill
bookkeeping. The method never calls Instant::now itself,
matching the sans-io rate-limiter pattern.
§Cancellation safety
This method is cancel-safe. Only a single mutex lock is taken and the bucket mutation is committed before the lock is released.
Sourcepub async fn with_handle_mut<F, R>(&self, peer: &SocketAddr, f: F) -> Option<R>where
F: FnOnce(&mut ClientHandle<P>) -> R,
pub async fn with_handle_mut<F, R>(&self, peer: &SocketAddr, f: F) -> Option<R>where
F: FnOnce(&mut ClientHandle<P>) -> R,
Run a closure with exclusive access to a peer’s handle.
Holds the internal Mutex for the duration of the closure;
keep the body short and avoid blocking operations inside it.
Returns None if no handle exists for the given peer (the
closure is not invoked in that case).
§Cancellation safety
This method is cancel-safe before the lock is acquired
and after the closure returns. While the closure is
executing it runs synchronously under the lock, so cancellation
during f is not possible. Do not call .await inside f
or this guarantee is lost.