MEDIUM: listener: index listener ID using compact trees

The listener ID is currently stored as a 32-bit int using an eb32 tree.
It's used essentially to find holes in order to automatically assign IDs,
and to detect duplicates. Let's change this to use compact trees instead
in order to save 24 bytes in struct listener for this node, plus 8 bytes
in struct proxy. The struct listener is now 704 bytes large, and the
struct proxy 3080.
This commit is contained in:
Willy Tarreau 2025-08-24 11:12:49 +02:00
parent 1a95bc42c7
commit 66191584d1
6 changed files with 16 additions and 22 deletions

View File

@ -238,7 +238,7 @@ struct listener {
enum obj_type obj_type; /* object type = OBJ_TYPE_LISTENER */
enum li_state state; /* state: NEW, INIT, ASSIGNED, LISTEN, READY, FULL */
uint16_t flags; /* listener flags: LI_F_* */
int luid; /* listener universally unique ID, used for SNMP */
int luid; /* listener universally unique ID, used for SNMP, indexed by <luid_node> below */
int nbconn; /* current number of connections on this listener */
unsigned long thr_idx; /* thread indexes for queue distribution (see listener_accept()) */
__decl_thread(HA_RWLOCK_T lock);
@ -253,10 +253,7 @@ struct listener {
struct list by_bind; /* chaining in bind_conf's list of listeners */
struct bind_conf *bind_conf; /* "bind" line settings, include SSL settings among other things */
struct receiver rx; /* network receiver parts */
struct {
struct eb32_node id; /* place in the tree of used IDs */
} conf; /* config information */
struct ceb_node luid_node; /* place in the tree of used IDs, indexes <luid> above */
struct guid_node guid; /* GUID global tree node */
struct li_per_thread *per_thr; /* per-thread fields (one per thread in the group) */

View File

@ -25,7 +25,7 @@
#include <stdlib.h>
#include <string.h>
#include <import/eb32tree.h>
#include <import/ceb32_tree.h>
#include <haproxy/api.h>
#include <haproxy/listener-t.h>
@ -242,7 +242,7 @@ extern ullong maxconn_reached;
/* index listener <li>'s id into proxy <px>'s used_listener_id */
static inline void listener_index_id(struct proxy *px, struct listener *li)
{
eb32_insert(&px->conf.used_listener_id, &li->conf.id);
ceb32_item_insert(&px->conf.used_listener_id, luid_node, luid, li);
}
static inline uint accept_queue_ring_len(const struct accept_queue_ring *ring)

View File

@ -462,7 +462,7 @@ struct proxy {
const char *file; /* file where the section appears */
struct ceb_node uuid_node; /* place in the tree of used IDs, indexes <uuid> above */
int line; /* line where the section appears */
struct eb_root used_listener_id;/* list of listener IDs in use */
struct ceb_root *used_listener_id; /* list of listener IDs in use */
struct eb_root used_server_id; /* list of server IDs in use */
struct ceb_root *used_server_name; /* list of server names in use */
struct list bind; /* list of bind settings */

View File

@ -4291,7 +4291,7 @@ init_proxies_list_stage2:
next_id = prev_li->luid + 1;
}
next_id = listener_get_next_id(curproxy, next_id);
listener->conf.id.key = listener->luid = next_id;
listener->luid = next_id;
listener_index_id(curproxy, listener);
}
next_id++;

View File

@ -405,11 +405,11 @@ void stop_listener(struct listener *l, int lpx, int lpr, int lli)
*/
uint listener_get_next_id(const struct proxy *px, uint from)
{
const struct eb32_node *used;
const struct listener *li;
do {
used = eb32_lookup_ge((struct eb_root*)&px->conf.used_listener_id, from);
if (!used || used->key > from)
li = ceb32_item_lookup_ge(&px->conf.used_listener_id, luid_node, luid, from, struct listener);
if (!li || li->luid > from)
return from; /* available */
from++;
} while (from);
@ -1870,9 +1870,9 @@ int bind_complete_thread_setup(struct bind_conf *bind_conf, int *err_code)
return cfgerr;
}
/* assign the ID to the first one only */
new_li->luid = new_li->conf.id.key = tmp_li->luid;
ceb32_item_delete(&fe->conf.used_listener_id, luid_node, luid, tmp_li);
new_li->luid = tmp_li->luid;
tmp_li->luid = 0;
eb32_delete(&tmp_li->conf.id);
if (new_li->luid)
listener_index_id(fe, new_li);
new_li = tmp_li;
@ -1894,9 +1894,9 @@ int bind_complete_thread_setup(struct bind_conf *bind_conf, int *err_code)
return cfgerr;
}
/* assign the ID to the first one only */
new_li->luid = new_li->conf.id.key = li->luid;
ceb32_item_delete(&fe->conf.used_listener_id, luid_node, luid, li);
new_li->luid = li->luid;
li->luid = 0;
eb32_delete(&li->conf.id);
if (new_li->luid)
listener_index_id(fe, new_li);
}
@ -2220,7 +2220,6 @@ static int bind_parse_guid_prefix(char **args, int cur_arg, struct proxy *px, st
/* parse the "id" bind keyword */
static int bind_parse_id(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
{
struct eb32_node *node;
struct listener *l, *new;
char *error;
@ -2240,16 +2239,14 @@ static int bind_parse_id(char **args, int cur_arg, struct proxy *px, struct bind
memprintf(err, "'%s' : expects an integer argument, found '%s'", args[cur_arg], args[cur_arg + 1]);
return ERR_ALERT | ERR_FATAL;
}
new->conf.id.key = new->luid;
if (new->luid <= 0) {
memprintf(err, "'%s' : custom id has to be > 0", args[cur_arg]);
return ERR_ALERT | ERR_FATAL;
}
node = eb32_lookup(&px->conf.used_listener_id, new->luid);
if (node) {
l = container_of(node, struct listener, conf.id);
l = ceb32_item_lookup(&px->conf.used_listener_id, luid_node, luid, new->luid, struct listener);
if (l) {
memprintf(err, "'%s' : custom id %d already used at %s:%d ('bind %s')",
args[cur_arg], l->luid, l->bind_conf->file, l->bind_conf->line,
l->bind_conf->arg);

View File

@ -1476,7 +1476,7 @@ void init_new_proxy(struct proxy *p)
MT_LIST_INIT(&p->lbprm.lb_free_list);
p->conf.used_listener_id = EB_ROOT;
p->conf.used_listener_id = NULL;
p->conf.used_server_id = EB_ROOT;
p->used_server_addr = NULL;