From aad2cbcde16e2c48e1e031ed476112c9e9f30763 Mon Sep 17 00:00:00 2001 From: Maurus Decimus <11444311+mdecimus@users.noreply.github.com> Date: Sun, 5 Jul 2026 12:02:26 +0200 Subject: [PATCH] Fix Network listener: Accept loop spins all CPU cores with no back-off when the process hits `EMFILE` (too many open files) --- CHANGELOG.md | 1 + crates/common/src/network/listen.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 021cd348..7dd82866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If - CalDAV: `calendar-query` REPORT returns invalid HTTP `404` when no events match the query. - Snowflake past id generation fails when the provided duration is longer than 4 years. - Calendar scheduling: Wrong RSVP base URL is used. +- Network listener: Accept loop spins all CPU cores with no back-off when the process hits `EMFILE` (too many open files). ## [0.16.11] - 2026-06-25 diff --git a/crates/common/src/network/listen.rs b/crates/common/src/network/listen.rs index ab6c060a..d8ec9307 100644 --- a/crates/common/src/network/listen.rs +++ b/crates/common/src/network/listen.rs @@ -17,6 +17,7 @@ use rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_128_GCM_SHA256; use std::{ net::{IpAddr, SocketAddr}, sync::Arc, + time::Duration, }; use store::registry::bootstrap::Bootstrap; use tokio::{net::TcpStream, sync::watch}; @@ -112,11 +113,17 @@ impl Listener { ), }; + const ACCEPT_BACKOFF: Duration = Duration::from_millis(5); + const MAX_ACCEPT_BACKOFF: Duration = Duration::from_secs(1); + let mut accept_backoff = ACCEPT_BACKOFF; + loop { tokio::select! { stream = listener.accept() => { match stream { Ok((stream, remote_addr)) => { + accept_backoff = ACCEPT_BACKOFF; + let server = inner.build_server(); let enable_acme = (is_https && server.has_acme_tls_providers()).then(|| server.clone()); @@ -160,6 +167,15 @@ impl Listener { } } Err(err) => { + if matches!( + err.kind(), + std::io::ErrorKind::ConnectionAborted + | std::io::ErrorKind::ConnectionReset + | std::io::ErrorKind::Interrupted + ) { + continue; + } + trc::event!( Network(trc::NetworkEvent::AcceptError), ListenerId = instance.id.clone(), @@ -168,6 +184,16 @@ impl Listener { Tls = is_tls, Reason = err.to_string(), ); + + tokio::select! { + _ = tokio::time::sleep(accept_backoff) => {} + _ = shutdown_rx.changed() => { + manager.shutdown().await; + break; + } + } + + accept_backoff = (accept_backoff * 2).min(MAX_ACCEPT_BACKOFF); } } },