From: Willy Tarreau Date: Wed, 26 Aug 2026 16:40:45 +0000 (+0200) Subject: OPTIM: server: remember the lowest known unused server ID X-Git-Tag: v3.5-dev6~58 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=f858386f637c659c72ad62225c0aa634f27c4de6;p=haproxy.git OPTIM: server: remember the lowest known unused server ID Tiburce Giroux reported that adding many servers without an explicit ID took time in O(N^2) for N servers due to the lookup of the lowest unused ID. Instead, let's just store this value after finding it, and restart from there upon next "add server". In a test with 20k servers the delay went down from 14.4 seconds to 66 milliseconds, or 218 times faster. And it takes 408ms to add 50000 servers (~120k srv/s). In practice we store the ID of the place to start searching from, so typically id+1 after a successful addition at the last position. Upon deletion we just reset the ID to the deleted server if it was before so that next addition starts searching from there again. Thanks to this, the lowest known hole is always the one allocated. It would make sense to backport this to 3.4 since it's being increasingly used in a dynamic way. --- diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h index be3fd3a7c..7b65e67a2 100644 --- a/include/haproxy/proxy-t.h +++ b/include/haproxy/proxy-t.h @@ -475,6 +475,7 @@ struct proxy { const char *file; /* file where the section appears */ struct ceb_node uuid_node; /* place in the tree of used IDs, indexes above */ int line; /* line where the section appears */ + uint first_unused_id; /* lowest ID to start searching unused ones from */ struct ceb_root *used_listener_id; /* list of listener IDs in use */ struct ceb_root *used_server_id; /* list of server IDs in use */ struct ceb_root *used_server_name; /* list of server names in use */ diff --git a/src/server.c b/src/server.c index d38e9d1ed..c7ed32d77 100644 --- a/src/server.c +++ b/src/server.c @@ -6742,11 +6742,12 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct * operation are completed. */ if (!srv->puid) { - srv->puid = server_get_next_id(be, 1); + srv->puid = server_get_next_id(be, be->conf.first_unused_id ? be->conf.first_unused_id : 1); if (!srv->puid) { ha_alert("Cannot attach server : no id left in proxy\n"); goto out; } + be->conf.first_unused_id = srv->puid; } if (!srv_alloc_lb(srv, be)) { @@ -6772,6 +6773,9 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct /* insert the server in the backend trees */ server_index_id(be, srv); + if (be->conf.first_unused_id == srv->puid) + be->conf.first_unused_id = srv->puid + 1; + _srv_register_name(srv, be); /* addr_key could be NULL if FQDN resolution is postponed (ie: add server from cli) */ if (srv->addr_key) @@ -7001,6 +7005,8 @@ static int cli_parse_delete_server(char **args, char *payload, struct appctx *ap proxy_take(srv->proxy); /* remove srv from addr_node tree */ + if (srv->puid < be->conf.first_unused_id) + be->conf.first_unused_id = srv->puid; // search from there for next add. ceb32_item_delete(&be->conf.used_server_id, conf.puid_node, puid, srv); cebuis_item_delete(&be->conf.used_server_name, conf.name_node, id, srv); cebuis_item_delete(&be->used_server_addr, addr_node, addr_key, srv);