fix(client): bound the threads a peer flood can create
One popular search produced a ConnectToPeer for every responder, and each one cost a 2 MiB wrapper thread, an unbounded 256 KiB actor, a listener thread per inbound socket, and on failure a 2 MiB thread that slept for 20 seconds. A container with a pids limit died on one album search (issues #32, #33). Four bounds, no behavior change for a quiet session: - P-type registrations run inline on the ops loop; only F transfers keep a worker thread. The dial already ran on the actor thread. - The broker timeout is a deadline swept by the ops loop instead of a sleeping thread per failed dial. - The registry caps live actors (default 512, Client::set_max_peers). At capacity the oldest peer outside the caller's protected set (live downloads, uploads, pending serves, queued messages, browses in flight) is evicted; if none qualify the registration is refused before any thread is spawned. - Concurrent listener handshakes are bounded by a 128-permit semaphore released once the peer-init arrives, so slow handshakes cannot multiply threads while transfers stay unthrottled. A replayed TransferResponse for an InProgress or Completed token is dropped instead of starting a second transfer thread over the same .part file. Fixes #32 Fixes #33
This commit is contained in:
@@ -50,10 +50,11 @@ impl Client {
|
||||
) = mpsc::channel();
|
||||
|
||||
let mut ctx = self.context.write_safe()?;
|
||||
let peer_registry = PeerRegistry::new(
|
||||
let peer_registry = PeerRegistry::with_capacity(
|
||||
ctx.actor_system.clone(),
|
||||
sender.clone(),
|
||||
self.username.clone(),
|
||||
ctx.max_peers.clone(),
|
||||
);
|
||||
ctx.peer_registry = Some(peer_registry);
|
||||
|
||||
@@ -175,6 +176,13 @@ impl Client {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_max_peers(&self, max_peers: usize) {
|
||||
if let Ok(ctx) = self.context.read_safe() {
|
||||
ctx.max_peers
|
||||
.store(max_peers.max(1), std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn remove_peer(&self, username: &str) {
|
||||
let context = match self.context.read_safe() {
|
||||
@@ -214,7 +222,10 @@ impl Client {
|
||||
}
|
||||
};
|
||||
if let Some(ref registry) = context.peer_registry {
|
||||
match registry.register_peer(peer_clone, stream, None) {
|
||||
let protected = context.protected_peers();
|
||||
match registry.register_peer_protected(
|
||||
peer_clone, stream, None, &protected,
|
||||
) {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
trace!(
|
||||
|
||||
Reference in New Issue
Block a user