From a59e6e5efd01a30ed4b83310ca8ba127f56488d0 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 19 May 2026 18:35:06 +0200 Subject: [PATCH] MINOR: server: support hash-key id32 for a cleaner distribution The "id" hash-key scales the ID by a factor of 16 that tries to leave room between the nodes on the 32-bit space to permit smooth weight variations (e.g. during slowstart). However this does not deal well with overlaps between server IDs. For example, assigning IDs that are only multiples of 256 million to 16 servers yields traffic only on one since in practice they all have the same 28 lower bits. The new "id32" hash key bridges this gap by using the full 32-bit ID of the server as the key. On the other hand, the user must be careful not to switch the hash function to "none" when using incremental IDs because in this case they might be very poorly distributed. But this can be convenient for automated provisionning systems which assign IDs themselves, as the full 32 bits are used now. --- doc/configuration.txt | 9 +++++++++ include/haproxy/server-t.h | 3 ++- src/lb_chash.c | 4 ++++ src/server.c | 7 +++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index c430a4796..ed0573165 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -18850,6 +18850,15 @@ hash-key better only use values comprised between 1 and this value to avoid overlap. + id32 The node keys will be derived from the server's numeric + identifier as set from "id" or which defaults to its position + in the server list, but the full 32 bits of the ID will be + used so that there is no collision. This one is not scaled + like "id" is, so it is recommended to either always use it + with a hash function (see "hash-key") or with explicitly + assigned ID values that are evenly distributed over the 32-bit + space. + addr The node keys will be derived from the server's address, when available, or else fall back on "id". diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h index 69265cb06..bbc548081 100644 --- a/include/haproxy/server-t.h +++ b/include/haproxy/server-t.h @@ -249,7 +249,8 @@ struct pid_list { /* srv methods of computing chash keys */ enum srv_hash_key { - SRV_HASH_KEY_ID = 0, /* derived from server puid */ + SRV_HASH_KEY_ID = 0, /* derived from server puid, 28 LSB used */ + SRV_HASH_KEY_ID32, /* derived from server puid, 32 bits used */ SRV_HASH_KEY_ADDR, /* derived from server address */ SRV_HASH_KEY_ADDR_PORT /* derived from server address and port */ }; diff --git a/src/lb_chash.c b/src/lb_chash.c index 362299d25..cf34370bf 100644 --- a/src/lb_chash.c +++ b/src/lb_chash.c @@ -121,6 +121,10 @@ static inline u32 chash_compute_server_key(struct server *s) } break; + case SRV_HASH_KEY_ID32: + key = full_hash(htonl(s->puid)); + break; + case SRV_HASH_KEY_ID: default: key = s->puid * SRV_EWGHT_RANGE; diff --git a/src/server.c b/src/server.c index 819e1c05a..4594e1a11 100644 --- a/src/server.c +++ b/src/server.c @@ -1015,13 +1015,16 @@ static int srv_parse_hash_key(char **args, int *cur_arg, struct proxy *curproxy, struct server *newsrv, char **err) { if (!args[*cur_arg + 1]) { - memprintf(err, "'%s expects 'id', 'addr', or 'addr-port' value", args[*cur_arg]); + memprintf(err, "'%s expects 'id', 'id32', 'addr', or 'addr-port' value", args[*cur_arg]); return ERR_ALERT | ERR_FATAL; } if (strcmp(args[*cur_arg + 1], "id") == 0) { newsrv->hash_key = SRV_HASH_KEY_ID; } + else if (strcmp(args[*cur_arg + 1], "id32") == 0) { + newsrv->hash_key = SRV_HASH_KEY_ID32; + } else if (strcmp(args[*cur_arg + 1], "addr") == 0) { newsrv->hash_key = SRV_HASH_KEY_ADDR; } @@ -1029,7 +1032,7 @@ static int srv_parse_hash_key(char **args, int *cur_arg, newsrv->hash_key = SRV_HASH_KEY_ADDR_PORT; } else { - memprintf(err, "'%s' has to be 'id', 'addr', or 'addr-port'", args[*cur_arg]); + memprintf(err, "'%s' has to be 'id', 'id32', 'addr', or 'addr-port'", args[*cur_arg]); return ERR_ALERT | ERR_FATAL; }