-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Hopefully this is the right place to open this issue in, it was spotted while using reqwest
but seems to be occuring due to how hyper
cleans up idle connections.
Main dependencies
[[package]]
name = "tokio"
version = "1.1.1"
[[package]]
name = "reqwest"
version = "0.11.0"
[[package]]
name = "hyper"
version = "0.14.2"
System
Linux 4.14.86-gentoo Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz
Hyper idle connection cleanup
We're using reqwest
at work in an application that was designed to just run forever. We also keep the same reqwest::Client
around the entire time the application is running.
While doing some loads testing, all HTTP requests started failing with the following error (don't worry about the random host name, we're not actually doing anything crazy, it's from the reproduction app):
error sending request for url (http://ypni4syq24hfujpserga:8080/): error trying to connect: dns error: Too many open files (os error 24)
It seems like the following code is responsible for cleaning up idle connections - when there are too many in the pool or when they've expired due to configured timeouts:
Line 591 in 4d2125c
fn checkout(&mut self, cx: &mut task::Context<'_>) -> Option<Pooled<T>> { |
However, IdlePopper::pop
, which does the actual cleanup, is never called if we never send a request to the same domain again, leaving the connection open forever, or at least long enough that the process hits Linux's file descriptor limit which is 1024 by default.
Sample reproduction application
Sorry for the messy approach but using HOSTALIASES
was the simplest/fastest way to get this working.
This application won't hit the file descriptor limit, it will just make 500 GET
requests to random domains and then sleep for 2 hours, to make it easier to observe how long the connections are left open for.
https://gist.github.com/glyphpoch/3f92005ae8b7d5ea0e3722ab10414f14
Running the app:
# Start an HTTP server
python -m http.server 12030
# Run the app
export HOSTALIASES=${PATH_TO_TEST_HOSTS}/testhosts
cargo run --release
# Observe process's file descriptors
pgrep reqtest
lsof -a -p ${PID_FROM_PGREP}
Workarounds
- While not ideal, recreating the
reqwest::Client
often, solves the issue.. - Disabling connection pooling works as well, e.g.:
let client= reqwest::Client::builder()
.pool_max_idle_per_host(0)
.build();