From 82cd5c13a55ec5827c03dd813723f27d161b45ef Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 29 Sep 2020 17:07:21 +0200 Subject: [PATCH] OPTIM: backend: skip LB when we know the backend is full For some algos (roundrobin, static-rr, leastconn, first) we know that if there is any request queued in the backend, it's because a previous attempt failed at finding a suitable server after trying all of them. This alone is sufficient to decide that the next request will skip the LB algo and directly reach the backend's queue. Doing this alone avoids an O(N) lookup when load-balancing on a saturated farm of N servers, which starts to be very expensive for hundreds of servers, especially under the lbprm lock. This change alone has increased the request rate from 110k to 148k RPS for 200 saturated servers on 8 threads, and fwlc_reposition_srv() doesn't show up anymore in perf top. See github issue #880 for more context. It could have been the same for random, except that random is performed using a consistent hash and it only considers a small set of servers (2 by default), so it may result in queueing at the backend despite having some free slots on unknown servers. It's no big deal though since random() only performs two attempts by default. For hashing algorithms this is pointless since we don't queue at the backend, except when there's no hash key found, which is the least of our concerns here. --- src/backend.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/backend.c b/src/backend.c index 35e6f781d..490dc1f28 100644 --- a/src/backend.c +++ b/src/backend.c @@ -640,14 +640,25 @@ int assign_server(struct stream *s) } } } - if (s->be->lbprm.algo & BE_LB_KIND) { + if (s->be->lbprm.algo & BE_LB_KIND) { /* we must check if we have at least one server available */ if (!s->be->lbprm.tot_weight) { err = SRV_STATUS_NOSRV; goto out; } + /* if there's some queue on the backend, with certain algos we + * know it's because all servers are full. + */ + if (s->be->nbpend && + (((s->be->lbprm.algo & BE_LB_KIND) == BE_LB_KIND_CB) || // conn-based: leastconn & first + ((s->be->lbprm.algo & (BE_LB_KIND|BE_LB_NEED|BE_LB_PARM)) == BE_LB_ALGO_RR) || // roundrobin + ((s->be->lbprm.algo & (BE_LB_KIND|BE_LB_NEED|BE_LB_PARM)) == BE_LB_ALGO_SRR))) { // static-rr + err = SRV_STATUS_FULL; + goto out; + } + /* First check whether we need to fetch some data or simply call * the LB lookup function. Only the hashing functions will need * some input data in fact, and will support multiple algorithms.