From: Aleksandar Lazic Date: Mon, 2 Nov 2026 17:00:00 +0000 (+0100) Subject: BUG/MEDIUM: leastconn: reset the smallest key when backups take over X-Git-Tag: v3.5-dev11~77 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=096b183f3ebe25c76b7f1398f83c407af333c746;p=haproxy.git BUG/MEDIUM: leastconn: reset the smallest key when backups take over Since commit 9fe72bba3 ("MAJOR: leastconn; Revamp the way servers are ordered."), the lookup starts at the smallest key recorded in lbprm.lb_seq, and this key is shared by the active and the backup trees. Under load it follows the keys of the active servers. When the last active server goes down or into maintenance, the key is only reset if it equals the key of that server, which is often not the case under load. With "option allbackups" the backup tree is then looked up from that key, and the idle backup servers, whose key is 0, are not found. fwlc_get_next_server() returns NULL, the streams are queued in the backend, and without maxconn nothing dequeues them: every request ends with a 503 after "timeout queue", although a backup server is up. It only recovers when a server is queued at a lower key again. Without "option allbackups" the first backup server is used directly and the problem does not happen. Fix it by resetting the smallest key when the last active server leaves the tree. This fixes GitHub issue #3555. It can be reproduced with an active and a backup server, "balance leastconn" and "option allbackups", by setting the active server to maintenance under load. This should be backported as far as 3.2. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Aleksandar Lazic --- diff --git a/src/lb_fwlc.c b/src/lb_fwlc.c index 8fd1e7383..e340fbaa7 100644 --- a/src/lb_fwlc.c +++ b/src/lb_fwlc.c @@ -575,6 +575,13 @@ static void fwlc_set_server_status_down(struct server *srv) fwlc_dequeue_srv(srv); fwlc_remove_from_tree(srv); + /* + * No more active server, we're switching to backups, let's reset + * the smallest key. + */ + if (!(srv->flags & SRV_F_BACKUP) && !p->srv_act) + fwlc_set_seq_and_smallest(&p->lbprm, p->lbprm.lb_seq, FWLC_LBPRM_SEQ(p->lbprm.lb_seq) + 1, 0); + out_update_backend: /* check/update tot_used, tot_weight */ update_backend_weight(p);