]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: lbprm: implement true "sticky" balance algo
authorAurelien DARRAGON <adarragon@haproxy.com>
Thu, 28 Mar 2024 16:24:53 +0000 (17:24 +0100)
committerAurelien DARRAGON <adarragon@haproxy.com>
Fri, 29 Mar 2024 16:08:37 +0000 (17:08 +0100)
As previously mentioned in cd352c0db ("MINOR: log/balance: rename
"log-sticky" to "sticky""), let's define a sticky algorithm that may be
used from any protocol. Sticky algorithm sticks on the same server as
long as it remains available.

The documentation was updated accordingly.

Makefile
doc/configuration.txt
include/haproxy/backend-t.h
include/haproxy/lb_ss-t.h [new file with mode: 0644]
include/haproxy/lb_ss.h [new file with mode: 0644]
src/backend.c
src/cfgparse.c
src/lb_ss.c [new file with mode: 0644]
src/log.c

index e2244982a2d764ff4580a715eaac5874921cec17..c904094dc45a7e55300711a63c35e597e6f27e37 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -939,7 +939,7 @@ OBJS += src/mux_h2.o src/mux_fcgi.o src/mux_h1.o src/tcpcheck.o               \
         src/ebmbtree.o src/cfgcond.o src/action.o src/xprt_handshake.o        \
         src/protocol.o src/proto_uxst.o src/proto_udp.o src/lb_map.o          \
         src/fix.o src/ev_select.o src/arg.o src/sock_inet.o src/event_hdl.o   \
-        src/mworker-prog.o src/hpack-dec.o src/cfgparse-tcp.o                 \
+        src/mworker-prog.o src/hpack-dec.o src/cfgparse-tcp.o src/lb_ss.o     \
         src/sock_unix.o src/shctx.o src/proto_uxdg.o src/fcgi.o               \
         src/eb64tree.o src/clock.o src/chunk.o src/cfgdiag.o src/signal.o     \
         src/regex.o src/lru.o src/eb32tree.o src/eb32sctree.o                 \
index 80778a1e0d0aed047107d4112148e4cb1345fdab..57f1a775f10f228966149e8f7af0d4fe17dc6547 100644 (file)
@@ -5366,8 +5366,7 @@ balance url_param <param> [check_post]
                   the log messages. When the server goes DOWN, the next server
                   in the list takes its place. When a previously DOWN server
                   goes back UP it is added at the end of the list so that the
-                  sticky server doesn't change until it becomes DOWN. This
-                  algorithm is only usable for backends in LOG mode.
+                  sticky server doesn't change until it becomes DOWN.
 
     <arguments> is an optional list of arguments which may be needed by some
                 algorithms. Right now, only "url_param", "uri" and "log-hash"
index 02a2cc5e4e55b1420a9bd32b60ff094582487eda..e556a3584d71dd94b6eb8ac0ec5cf795d6e6b31f 100644 (file)
@@ -28,6 +28,7 @@
 #include <haproxy/lb_fwlc-t.h>
 #include <haproxy/lb_fwrr-t.h>
 #include <haproxy/lb_map-t.h>
+#include <haproxy/lb_ss-t.h>
 #include <haproxy/server-t.h>
 #include <haproxy/thread-t.h>
 
@@ -58,6 +59,9 @@
 #define BE_LB_CB_LC     0x00000000  /* least-connections */
 #define BE_LB_CB_FAS    0x00000001  /* first available server (opposite of leastconn) */
 
+/* BE_LB_SA_* is used with BE_LB_KIND_SA */
+#define BE_LB_SA_SS     0x00000000  /* stick to server as long as it is available */
+
 #define BE_LB_PARM      0x000000FF  /* mask to get/clear the LB param */
 
 /* Required input(s) */
@@ -73,6 +77,7 @@
 #define BE_LB_KIND_RR   0x00010000  /* round-robin */
 #define BE_LB_KIND_CB   0x00020000  /* connection-based */
 #define BE_LB_KIND_HI   0x00030000  /* hash of input (see hash inputs above) */
+#define BE_LB_KIND_SA   0x00040000  /* standalone (specific algorithms, cannot be grouped) */
 #define BE_LB_KIND      0x00070000  /* mask to get/clear LB algorithm */
 
 /* All known variants of load balancing algorithms. These can be cleared using
@@ -83,6 +88,7 @@
 #define BE_LB_ALGO_RND  (BE_LB_KIND_RR | BE_LB_NEED_NONE | BE_LB_RR_RANDOM) /* random value */
 #define BE_LB_ALGO_LC   (BE_LB_KIND_CB | BE_LB_NEED_NONE | BE_LB_CB_LC)    /* least connections */
 #define BE_LB_ALGO_FAS  (BE_LB_KIND_CB | BE_LB_NEED_NONE | BE_LB_CB_FAS)   /* first available server */
+#define BE_LB_ALGO_SS   (BE_LB_KIND_SA | BE_LB_NEED_NONE | BE_LB_SA_SS)    /* sticky */
 #define BE_LB_ALGO_SRR  (BE_LB_KIND_RR | BE_LB_NEED_NONE | BE_LB_RR_STATIC) /* static round robin */
 #define BE_LB_ALGO_SH  (BE_LB_KIND_HI | BE_LB_NEED_ADDR | BE_LB_HASH_SRC) /* hash: source IP */
 #define BE_LB_ALGO_UH  (BE_LB_KIND_HI | BE_LB_NEED_HTTP | BE_LB_HASH_URI) /* hash: HTTP URI  */
@@ -91,7 +97,6 @@
 #define BE_LB_ALGO_RCH (BE_LB_KIND_HI | BE_LB_NEED_DATA | BE_LB_HASH_RDP) /* hash: RDP cookie value   */
 #define BE_LB_ALGO_SMP (BE_LB_KIND_HI | BE_LB_NEED_DATA | BE_LB_HASH_SMP) /* hash: sample expression  */
 #define BE_LB_ALGO_LH  (BE_LB_KIND_HI | BE_LB_NEED_LOG  | BE_LB_HASH_SMP) /* log hash: sample expression  */
-#define BE_LB_ALGO_LS  (BE_LB_KIND_CB | BE_LB_NEED_LOG  | BE_LB_CB_FAS)   /* log sticky */
 #define BE_LB_ALGO      (BE_LB_KIND    | BE_LB_NEED      | BE_LB_PARM    ) /* mask to clear algo */
 
 /* Higher bits define how a given criterion is mapped to a server. In fact it
@@ -147,6 +152,7 @@ struct lbprm {
                struct lb_fwlc fwlc;
                struct lb_chash chash;
                struct lb_fas fas;
+               struct lb_ss ss;
                struct {
                        struct server   **srv;  /* array containing in-use log servers */
                        struct list     avail;  /* servers available for lb are registered in this list */
diff --git a/include/haproxy/lb_ss-t.h b/include/haproxy/lb_ss-t.h
new file mode 100644 (file)
index 0000000..9014bce
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * include/haproxy/lb_ss-t.h
+ * Types for sticky load-balancing
+ *
+ * Copyright 2024 HAProxy Technologies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _HAPROXY_LB_LH_T_H
+#define _HAPROXY_LB_LH_T_H
+
+#include <haproxy/api-t.h>
+#include <haproxy/server-t.h>
+
+struct lb_ss {
+       struct server  *srv;    /* sticked server */
+};
+
+#endif /* _HAPROXY_LB_LH_T_H */
diff --git a/include/haproxy/lb_ss.h b/include/haproxy/lb_ss.h
new file mode 100644 (file)
index 0000000..6ec3153
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * include/haproxy/lb_ss.h
+ * sticky load-balancing
+ *
+ * Copyright 2024 HAProxy Technologies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _HAPROXY_LB_SS_H
+#define _HAPROXY_LB_SS_H
+
+#include <haproxy/api.h>
+#include <haproxy/proxy-t.h>
+#include <haproxy/server-t.h>
+
+void recalc_server_ss(struct proxy *px);
+void init_server_ss(struct proxy *px);
+struct server *ss_get_server(struct proxy *px);
+
+#endif /* _HAPROXY_LB_SS_H */
index 18df7ea0f40df919d2226bd8b67b217a1da2db63..aaee78686036aea697a149b333cbe70c8e839103 100644 (file)
@@ -39,6 +39,7 @@
 #include <haproxy/lb_fwlc.h>
 #include <haproxy/lb_fwrr.h>
 #include <haproxy/lb_map.h>
+#include <haproxy/lb_ss.h>
 #include <haproxy/log.h>
 #include <haproxy/namespace.h>
 #include <haproxy/obj_type.h>
@@ -813,6 +814,14 @@ int assign_server(struct stream *s)
                        break;
 
                default:
+                       if ((s->be->lbprm.algo & BE_LB_KIND) == BE_LB_KIND_SA) {
+                               /* some special algos that cannot be grouped together */
+
+                               if ((s->be->lbprm.algo & BE_LB_PARM) == BE_LB_SA_SS)
+                                       srv = ss_get_server(s->be);
+
+                               break;
+                       }
                        /* unknown balancing algorithm */
                        err = SRV_STATUS_INTERNAL;
                        goto out;
@@ -2878,7 +2887,7 @@ int backend_parse_balance(const char **args, char **err, struct proxy *curproxy)
        }
        else if (strcmp(args[0], "sticky") == 0) {
                curproxy->lbprm.algo &= ~BE_LB_ALGO;
-               curproxy->lbprm.algo |= BE_LB_ALGO_LS;
+               curproxy->lbprm.algo |= BE_LB_ALGO_SS;
        }
        else {
                memprintf(err, "only supports 'roundrobin', 'static-rr', 'leastconn', 'source', 'uri', 'url_param', 'hash', 'hdr(name)', 'rdp-cookie(name)', 'log-hash' and 'sticky' options.");
index e9e4e8a7eeb8b7f286040c76296baf8d198e4900..69830c33bca076246e1488c63e8d000b322869ac 100644 (file)
@@ -64,6 +64,7 @@
 #include <haproxy/lb_fwlc.h>
 #include <haproxy/lb_fwrr.h>
 #include <haproxy/lb_map.h>
+#include <haproxy/lb_ss.h>
 #include <haproxy/listener.h>
 #include <haproxy/log.h>
 #include <haproxy/sink.h>
@@ -3766,6 +3767,12 @@ out_uri_auth_compat:
                                init_server_map(curproxy);
                        }
                        break;
+               case BE_LB_KIND_SA:
+                       if ((curproxy->lbprm.algo & BE_LB_PARM) == BE_LB_SA_SS) {
+                               curproxy->lbprm.algo |= BE_LB_PROP_DYN;
+                               init_server_ss(curproxy);
+                       }
+                       break;
                }
  skip_server_lb_init:
                HA_RWLOCK_INIT(&curproxy->lbprm.lock);
diff --git a/src/lb_ss.c b/src/lb_ss.c
new file mode 100644 (file)
index 0000000..4af031b
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * sticky load-balancing
+ *
+ * Copyright 2024 HAProxy Technologies
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <haproxy/api.h>
+#include <haproxy/backend.h>
+#include <haproxy/lb_ss.h>
+#include <haproxy/server-t.h>
+
+/* this function updates the stick server according to server <srv>'s new state.
+ *
+ * The server's lock must be held. The lbprm's lock will be used.
+ */
+static void ss_set_server_status_down(struct server *srv)
+{
+       struct proxy *p = srv->proxy;
+
+       if (!srv_lb_status_changed(srv))
+               return;
+
+       if (srv_willbe_usable(srv))
+               goto out_update_state;
+
+       HA_RWLOCK_WRLOCK(LBPRM_LOCK, &p->lbprm.lock);
+
+       if (!srv_currently_usable(srv))
+               /* server was already down */
+               goto out_update_backend;
+
+       if (srv->flags & SRV_F_BACKUP) {
+               p->lbprm.tot_wbck -= srv->cur_eweight;
+               p->srv_bck--;
+       } else {
+               p->lbprm.tot_wact -= srv->cur_eweight;
+               p->srv_act--;
+       }
+       if (srv == p->lbprm.ss.srv) {
+               /* sticked server is down, elect a new server
+                * that we will be sticking on.
+                */
+               recalc_server_ss(p);
+       }
+
+ out_update_backend:
+       /* check/update tot_used, tot_weight */
+       update_backend_weight(p);
+       HA_RWLOCK_WRUNLOCK(LBPRM_LOCK, &p->lbprm.lock);
+
+ out_update_state:
+       srv_lb_commit_status(srv);
+}
+
+/* This function updates the stick server according to server <srv>'s new state.
+ *
+ * The server's lock must be held. The lbprm's lock will be used.
+ */
+static void ss_set_server_status_up(struct server *srv)
+{
+       struct proxy *p = srv->proxy;
+
+       if (!srv_lb_status_changed(srv))
+               return;
+
+       if (!srv_willbe_usable(srv))
+               goto out_update_state;
+
+       HA_RWLOCK_WRLOCK(LBPRM_LOCK, &p->lbprm.lock);
+
+       if (srv_currently_usable(srv))
+               /* server was already up */
+               goto out_update_backend;
+
+       if (srv->flags & SRV_F_BACKUP) {
+               p->lbprm.tot_wbck += srv->next_eweight;
+               p->srv_bck++;
+       } else {
+               p->lbprm.tot_wact += srv->next_eweight;
+               p->srv_act++;
+       }
+       if (!p->lbprm.ss.srv ||
+           ((p->lbprm.ss.srv->flags & SRV_F_BACKUP) && !(srv->flags & SRV_F_BACKUP))) {
+               /* we didn't have a server or were sticking on a backup server,
+                * but now we have an active server, let's switch to it
+                */
+               p->lbprm.ss.srv = srv;
+       }
+
+ out_update_backend:
+       /* check/update tot_used, tot_weight */
+       update_backend_weight(p);
+       HA_RWLOCK_WRUNLOCK(LBPRM_LOCK, &p->lbprm.lock);
+
+ out_update_state:
+       srv_lb_commit_status(srv);
+}
+
+/* This function elects a new stick server for proxy px.
+ *
+ * The lbprm's lock must be held.
+ */
+void recalc_server_ss(struct proxy *px)
+{
+       struct server *cur, *first;
+       int flag;
+
+       if (!px->lbprm.tot_used)
+               return; /* no server */
+
+       /* here we *know* that we have some servers */
+       if (px->srv_act)
+               flag = 0;
+       else
+               flag = SRV_F_BACKUP;
+
+       first = NULL;
+
+       for (cur = px->srv; cur; cur = cur->next) {
+               if ((cur->flags & SRV_F_BACKUP) == flag &&
+                   srv_willbe_usable(cur)) {
+                       first = cur;
+                       break;
+               }
+       }
+       px->lbprm.ss.srv = first;
+}
+
+/* This function is responsible for preparing sticky LB algorithm.
+ * It should be called only once per proxy, at config time.
+ */
+void init_server_ss(struct proxy *p)
+{
+       struct server *srv;
+
+       p->lbprm.set_server_status_up   = ss_set_server_status_up;
+       p->lbprm.set_server_status_down = ss_set_server_status_down;
+       p->lbprm.update_server_eweight = NULL;
+
+       if (!p->srv)
+               return;
+
+       for (srv = p->srv; srv; srv = srv->next) {
+               srv->next_eweight = 1; /* ignore weights, all servers have the same weight */
+               srv_lb_commit_status(srv);
+       }
+
+       /* recounts servers and their weights */
+       recount_servers(p);
+       update_backend_weight(p);
+       recalc_server_ss(p);
+}
+
+/*
+ * This function returns the server that we're sticking on. If any server
+ * is found, it will be returned. If no valid server is found, NULL is
+ * returned.
+ *
+ * The lbprm's lock will be used.
+ */
+struct server *ss_get_server(struct proxy *px)
+{
+       struct server *srv = NULL;
+
+       HA_RWLOCK_RDLOCK(LBPRM_LOCK, &px->lbprm.lock);
+       srv = px->lbprm.ss.srv;
+       HA_RWLOCK_RDUNLOCK(LBPRM_LOCK, &px->lbprm.lock);
+       return srv;
+}
index c1e44779b254b6f7f9d72fa4bd4de50e15df1018..e9f26d67e270b972d919951537c12c1dae37103a 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -981,7 +981,7 @@ static int _postcheck_log_backend_compat(struct proxy *be)
        }
        if (balance_algo != BE_LB_ALGO_RR &&
            balance_algo != BE_LB_ALGO_RND &&
-           balance_algo != BE_LB_ALGO_LS &&
+           balance_algo != BE_LB_ALGO_SS &&
            balance_algo != BE_LB_ALGO_LH) {
                ha_alert("in %s '%s': \"balance\" only supports 'roundrobin', 'random', 'sticky' and 'log-hash'.\n", proxy_type_str(be), be->id);
                err_code |= ERR_ALERT | ERR_FATAL;
@@ -2328,7 +2328,7 @@ static inline void __do_send_log_backend(struct proxy *be, struct log_header hdr
                 */
                targetid = HA_ATOMIC_FETCH_ADD(&be->lbprm.log.lastid, 1) % nb_srv;
        }
-       else if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_LS) {
+       else if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_SS) {
                /* sticky mode: use first server in the pool, which will always stay
                 * first during dequeuing and requeuing, unless it becomes unavailable
                 * and will be replaced by another one