← Commit list

0f48cf3d7f

Stefan Eissing
Stefan Eissing
2026-08-31
SUCCESS Duration: 390s

Subject

conncache: guess maxconnects different

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md+8 -3
@@ -28,14 +28,19 @@ CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_MAXCONNECTS, long max);
Pass a long indicating the **max**, the maximum amount of connections that
libcurl may keep alive in its connection cache after use. By default libcurl
-enlarges the size for each added easy handle to make it fit 4 times the number
+enlarges the size for each added easy handle to make it fit twice the number
of added easy handles.
By setting this option, you prevent the cache size from growing beyond the
limit set by you.
-When the cache is full, curl closes the oldest connection present in the cache
-to prevent the number of connections from increasing.
+When the cache is full on a set limit, curl closes the oldest connection
+present in the cache to prevent the number of connections from increasing.
+
+When the cache is full on the default limit, curl closes the oldest connection
+present in the cache only if it has not been used for at least a second. This
+is done because the number of added transfer can vary greatly, depending on
+how the application uses them.
This option is for the multi handle's use only, when using the easy interface
you should instead use the CURLOPT_MAXCONNECTS(3) option.
lib/conncache.c+20 -20
@@ -339,15 +339,16 @@ static struct connectdata *cpool_bundle_get_oldest_idle(
}
static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool,
- const struct curltime *pnow)
+ const struct curltime *pnow,
+ timediff_t min_age_ms)
{
struct Curl_hash_iterator iter;
struct Curl_llist_node *curr;
struct Curl_hash_element *he;
- struct connectdata *oldest_idle = NULL;
struct cpool_bundle *bundle;
- timediff_t highscore = -1;
- timediff_t score;
+ struct connectdata *oldest_idle = NULL;
+ timediff_t oldest_idle_ms = -1;
+ timediff_t idle_ms;
Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
@@ -361,10 +362,9 @@ static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool,
conn = Curl_node_elem(curr);
if(CONN_INUSE(conn) || conn->bits.close || conn->bits.connect_only)
continue;
- /* Set higher score for the age passed since the connection was used */
- score = curlx_ptimediff_ms(pnow, &conn->lastused);
- if(score > highscore) {
- highscore = score;
+ idle_ms = curlx_ptimediff_ms(pnow, &conn->lastused);
+ if((idle_ms >= min_age_ms) && (idle_ms > oldest_idle_ms)) {
+ oldest_idle_ms = idle_ms;
oldest_idle = conn;
}
}
@@ -451,7 +451,8 @@ static void cpool_evict_conn(struct cpool *cpool,
}
int Curl_cpool_check_limits(struct Curl_easy *data,
- struct connectdata *conn)
+ struct connectdata *conn,
+ const struct curltime *pnow)
{
struct cpool *cpool = cpool_get_instance(data);
struct cshutdn *cshutdn = Curl_cshutdn_get(data);
@@ -494,8 +495,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
struct connectdata *oldest_idle = NULL;
/* The bundle is full. Extract the oldest connection that may
* be removed now, if there is one. */
- oldest_idle = cpool_bundle_get_oldest_idle(bundle,
- Curl_pgrs_now(data));
+ oldest_idle = cpool_bundle_get_oldest_idle(bundle, pnow);
if(!oldest_idle)
break;
/* disconnect the old conn and continue */
@@ -527,7 +527,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
}
else {
struct connectdata *oldest_idle =
- cpool_get_oldest_idle(cpool, Curl_pgrs_now(data));
+ cpool_get_oldest_idle(cpool, pnow, 0);
if(!oldest_idle)
break;
/* disconnect the old conn and continue */
@@ -646,18 +646,18 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
struct cpool *cpool = cpool_get_instance(data);
struct Curl_easy *admin;
bool kept = TRUE;
+ timediff_t min_age_ms = 0;
if(!data || !data->multi)
return kept;
if(!data->multi->maxconnects) {
- /* Running transfers is a weak indicator of business. When many
- * transfers are done at the same time (parallel h1), the number
- * may drop quite low and we do not want to close connections
- * just to have another wave of transfers arriving next. */
- uint32_t running = Curl_multi_xfers_running(data->multi);
- running = CURLMAX(running, 128);
- maxconnects = (running <= UINT_MAX / 4) ? running * 4 : UINT_MAX;
+ /* Attached transfers is a weak indicator of business. */
+ uint32_t attached = Curl_multi_xfers_attached(data->multi);
+ maxconnects = (attached <= UINT_MAX / 2) ? attached * 2 : UINT_MAX;
+ /* We are guessing. So, only evict a "seemingly superfluous" connection
+ * when has not been used for this long, */
+ min_age_ms = 1000;
}
else {
maxconnects = data->multi->maxconnects;
@@ -676,7 +676,7 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
infof(data, "Connection pool is full, closing the oldest of %zu/%u",
cpool->num_conn, maxconnects);
- oldest_idle = cpool_get_oldest_idle(cpool, Curl_pgrs_now(data));
+ oldest_idle = cpool_get_oldest_idle(cpool, &conn->lastused, min_age_ms);
kept = (oldest_idle != conn);
if(oldest_idle) {
cpool_evict_conn(cpool, admin, oldest_idle);
lib/conncache.h+2 -1
@@ -93,7 +93,8 @@ CURLcode Curl_cpool_add(struct Curl_easy *data,
#define CPOOL_LIMIT_DEST 1
#define CPOOL_LIMIT_TOTAL 2
int Curl_cpool_check_limits(struct Curl_easy *data,
- struct connectdata *conn);
+ struct connectdata *conn,
+ const struct curltime *pnow);
/* Return of conn is suitable. If so, stops iteration. */
typedef bool Curl_cpool_conn_match_cb(struct connectdata *conn,
lib/multi.c+16 -5
@@ -2951,7 +2951,7 @@ static CURLMcode multi_perform(struct Curl_multi *multi,
}
if(running_handles) {
- unsigned int running = Curl_multi_xfers_running(multi);
+ uint32_t running = Curl_multi_xfers_running(multi);
*running_handles = (running < INT_MAX) ? (int)running : INT_MAX;
}
@@ -3309,7 +3309,7 @@ out:
mresult = Curl_mntfy_dispatch_all(multi);
if(running_handles) {
- unsigned int running = Curl_multi_xfers_running(multi);
+ uint32_t running = Curl_multi_xfers_running(multi);
*running_handles = (running < INT_MAX) ? (int)running : INT_MAX;
}
@@ -4153,14 +4153,25 @@ bool Curl_multi_knows_easy(struct Curl_multi *multi, struct Curl_easy *data)
return Curl_uint32_tbl_get(&multi->xfers, data->mid) == data;
}
-unsigned int Curl_multi_xfers_running(struct Curl_multi *multi)
+uint32_t Curl_multi_xfers_running(struct Curl_multi *multi)
{
- DEBUGASSERT(multi);
- if(!multi)
+ if(!multi) {
+ DEBUGASSERT(0);
return 0;
+ }
return multi->xfers_alive;
}
+uint32_t Curl_multi_xfers_attached(struct Curl_multi *multi)
+{
+ if(!multi || !multi->admin) {
+ DEBUGASSERT(0);
+ return 0;
+ }
+ /* Discount the admin handle */
+ return Curl_uint32_tbl_count(&multi->xfers) - 1;
+}
+
void Curl_multi_mark_dirty(struct Curl_easy *data)
{
if(data->multi && data->mid != UINT32_MAX)
lib/multiif.h+5 -1
@@ -158,8 +158,12 @@ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi,
/* TRUE if multi knows about data via its `mid` */
bool Curl_multi_knows_easy(struct Curl_multi *multi, struct Curl_easy *data);
+/* Get the # of transfers attached to the multi, without the internal
+ * admin handle. */
+uint32_t Curl_multi_xfers_attached(struct Curl_multi *multi);
+
/* Get the # of transfers current in process/pending. */
-unsigned int Curl_multi_xfers_running(struct Curl_multi *multi);
+uint32_t Curl_multi_xfers_running(struct Curl_multi *multi);
/* Mark a transfer as dirty, e.g. to be rerun at earliest convenience.
* A cheap operation, can be done many times repeatedly. */
lib/url.c+1 -1
@@ -2383,7 +2383,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data)
goto out;
}
else {
- switch(Curl_cpool_check_limits(data, needle)) {
+ switch(Curl_cpool_check_limits(data, needle, &needle->created)) {
case CPOOL_LIMIT_DEST:
infof(data, "No more connections allowed to host");
result = CURLE_NO_CONNECTION_AVAILABLE;