From 7004bb3b8c55bd901ef2e5032d77a105ec4256f5 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 19 May 2026 19:06:32 +0200 Subject: [PATCH] MINOR: backend: support hash-key guid for a stabler distribution When server fleets are constantly updated, using a stable distribution across a bunch of load balancers can be convenient. The addr and port already provide a bit of this but for situations were addresses might differ between sites or change dynamically this does not work. The guid is perfect for this because by definition it's supposed to designate a single server and be unique. So when two servers anywhere have the same, the tool that provisionned them promises that they are the same server. So here we introduce "hash-key guid" which performs a 32-bit hash on the GUID value. When no guid is provided, a fallback is performed on ID, as is done for other keys. --- doc/configuration.txt | 6 ++++++ include/haproxy/server-t.h | 1 + src/lb_chash.c | 11 +++++++++++ src/server.c | 7 +++++-- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index ed0573165..bfed93698 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -18859,6 +18859,12 @@ hash-key assigned ID values that are evenly distributed over the 32-bit space. + guid The node keys will be derived from the server's guid, when + available, otherwise they will fall back on "id". The benefit + is that it does not depend on ordering at all, only on an + internal stable identifier that can be replicated across many + load balancers. + 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 bbc548081..a3dbc606a 100644 --- a/include/haproxy/server-t.h +++ b/include/haproxy/server-t.h @@ -251,6 +251,7 @@ struct pid_list { enum srv_hash_key { 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_GUID, /* derived from server guid */ 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 cf34370bf..ce60bee8b 100644 --- a/src/lb_chash.c +++ b/src/lb_chash.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,7 @@ static inline u32 chash_compute_server_key(struct server *s) { enum srv_hash_key hash_key = s->hash_key; struct server_inetaddr srv_addr; + const char *guid_key = NULL; u32 key; /* If hash-key is addr or addr-port then we need the address, but if we @@ -96,6 +98,11 @@ static inline u32 chash_compute_server_key(struct server *s) } break; + case SRV_HASH_KEY_GUID: + guid_key = guid_get(&s->guid); + if (!guid_key) + hash_key = SRV_HASH_KEY_ID; + break; default: break; } @@ -121,6 +128,10 @@ static inline u32 chash_compute_server_key(struct server *s) } break; + case SRV_HASH_KEY_GUID: + key = XXH32(guid_key, strlen(guid_key), 0); + break; + case SRV_HASH_KEY_ID32: key = full_hash(htonl(s->puid)); break; diff --git a/src/server.c b/src/server.c index 4594e1a11..9ebce0c8c 100644 --- a/src/server.c +++ b/src/server.c @@ -1015,7 +1015,7 @@ 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', 'id32', 'addr', or 'addr-port' value", args[*cur_arg]); + memprintf(err, "'%s expects 'id', 'id32', 'guid', 'addr', or 'addr-port' value", args[*cur_arg]); return ERR_ALERT | ERR_FATAL; } @@ -1025,6 +1025,9 @@ static int srv_parse_hash_key(char **args, int *cur_arg, else if (strcmp(args[*cur_arg + 1], "id32") == 0) { newsrv->hash_key = SRV_HASH_KEY_ID32; } + else if (strcmp(args[*cur_arg + 1], "guid") == 0) { + newsrv->hash_key = SRV_HASH_KEY_GUID; + } else if (strcmp(args[*cur_arg + 1], "addr") == 0) { newsrv->hash_key = SRV_HASH_KEY_ADDR; } @@ -1032,7 +1035,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', 'id32', 'addr', or 'addr-port'", args[*cur_arg]); + memprintf(err, "'%s' has to be 'id', 'id32', 'guid', 'addr', or 'addr-port'", args[*cur_arg]); return ERR_ALERT | ERR_FATAL; }