From 6be02d1c6eed0e35e43ca35697501ba688a4da8f Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sat, 17 May 2025 09:54:49 +0200 Subject: [PATCH] BUG/MAJOR: leastconn: do not loop forever when facing saturated servers Since commit 9fe72bba3 ("MAJOR: leastconn; Revamp the way servers are ordered."), there's no way to escape the loop visiting the mt_list heads in fwlc_get_next_server if all servers in the list are saturated, resulting in a watchdog panic. It can be reproduced with this config and injecting with more than 2 concurrent conns: balance leastconn server s1 127.0.0.1:8000 maxconn 1 server s2 127.0.0.1:8000 maxconn 1 Here we count the number of saturated servers that were encountered, and escape the loop once the number of remaining servers exceeds the number of saturated ones. No backport is needed since this arrived in 3.2. --- src/lb_fwlc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lb_fwlc.c b/src/lb_fwlc.c index 1c5717ba6..9023a7aaf 100644 --- a/src/lb_fwlc.c +++ b/src/lb_fwlc.c @@ -762,13 +762,14 @@ struct server *fwlc_get_next_server(struct proxy *p, struct server *srvtoavoid) while (node) { struct fwlc_tree_elt *tree_elt; struct server *s; + int unusable = 0; int orig_nb; int i = 0; tree_elt = eb32_entry(node, struct fwlc_tree_elt, lb_node); orig_nb = statistical_prng_range(FWLC_LISTS_NB); - while (_HA_ATOMIC_LOAD(&tree_elt->elements) > 0) { + while (_HA_ATOMIC_LOAD(&tree_elt->elements) > unusable) { struct mt_list mt_list; mt_list.next = _HA_ATOMIC_LOAD(&tree_elt->srv_list[(i + orig_nb) % FWLC_LISTS_NB].next); @@ -802,6 +803,8 @@ struct server *fwlc_get_next_server(struct proxy *p, struct server *srvtoavoid) } avoided = s; } + else + unusable++; i++; } else if (mt_list.next == &tree_elt->srv_list[(i + orig_nb) % FWLC_LISTS_NB]) { i++;