diff --git a/Makefile b/Makefile
index e2244982a..c904094dc 100644
--- 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 \
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 80778a1e0..57f1a775f 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -5366,8 +5366,7 @@ balance url_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.
is an optional list of arguments which may be needed by some
algorithms. Right now, only "url_param", "uri" and "log-hash"
diff --git a/include/haproxy/backend-t.h b/include/haproxy/backend-t.h
index 02a2cc5e4..e556a3584 100644
--- a/include/haproxy/backend-t.h
+++ b/include/haproxy/backend-t.h
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
#include
#include
@@ -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
index 000000000..9014bce0e
--- /dev/null
+++ b/include/haproxy/lb_ss-t.h
@@ -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
+#include
+
+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
index 000000000..6ec31531f
--- /dev/null
+++ b/include/haproxy/lb_ss.h
@@ -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
+#include
+#include
+
+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 */
diff --git a/src/backend.c b/src/backend.c
index 18df7ea0f..aaee78686 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -39,6 +39,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -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.");
diff --git a/src/cfgparse.c b/src/cfgparse.c
index e9e4e8a7e..69830c33b 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -64,6 +64,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -3766,6 +3767,12 @@ int check_config_validity()
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
index 000000000..4af031b29
--- /dev/null
+++ b/src/lb_ss.c
@@ -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
+#include
+#include
+#include
+
+/* this function updates the stick server according to server '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 '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;
+}
diff --git a/src/log.c b/src/log.c
index c1e44779b..e9f26d67e 100644
--- 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