]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: listener: support another thread dispatch mode: "fair"
authorWilly Tarreau <w@1wt.eu>
Thu, 20 Apr 2023 13:40:38 +0000 (15:40 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 21 Apr 2023 15:41:26 +0000 (17:41 +0200)
This new algorithm for rebalancing incoming connections to multiple
threads is simpler and instead of considering the threads load, it will
only cycle through all of them, offering a fair share of the traffic to
each thread. It may be well suited for short-lived connections but is
also convenient for very large thread counts where it's not always certain
that the least loaded thread will always be found.

doc/configuration.txt
include/haproxy/global-t.h
src/haproxy.c
src/listener.c

index 82721f43af713a5a41245727f4213dd9d3a26036..a4c9672678211323a216fb22ee3c8e25aac76095 100644 (file)
@@ -3023,16 +3023,23 @@ tune.idletimer <timeout>
   clicking). There should be no reason for changing this value. Please check
   tune.ssl.maxrecord below.
 
-tune.listener.multi-queue { on | off }
-  Enables ('on') or disables ('off') the listener's multi-queue accept which
-  spreads the incoming traffic to all threads a "bind" line is allowed to run
-  on instead of taking them for itself. This provides a smoother traffic
+tune.listener.multi-queue { on | fair | off }
+  Enables ('on' / 'fair') or disables ('off') the listener's multi-queue accept
+  which spreads the incoming traffic to all threads a "bind" line is allowed to
+  run on instead of taking them for itself. This provides a smoother traffic
   distribution and scales much better, especially in environments where threads
   may be unevenly loaded due to external activity (network interrupts colliding
-  with one thread for example). This option is enabled by default, but it may
-  be forcefully disabled for troubleshooting or for situations where it is
-  estimated that the operating system already provides a good enough
-  distribution and connections are extremely short-lived.
+  with one thread for example). The default mode, "on", optimizes the choice of
+  a thread by picking in a sample the one with the less connections. It is
+  often the best choice when connections are long-lived as it manages to keep
+  all threads busy. A second mode, "fair", instead cycles through all threads
+  regardless of their instant load level. It can be better suited for short-
+  lived connections, or on machines with very large numbers of threads where
+  the probability to find the least loaded thread with the first mode is low.
+  Finally it is possible to forcefully disable the redistribution mechanism
+  using "off" for troubleshooting, or for situations where connections are
+  short-lived and it is estimated that the operating system alredy provides a
+  good enough distribution. The default is "on".
 
 tune.lua.forced-yield <number>
   This directive forces the Lua engine to execute a yield each <number> of
index b7b00bab035ce955ddd0acdcea72c46fd203f1d1..af7f26cfb1a02a67180bfe57f52668e93246c8ca 100644 (file)
@@ -66,7 +66,7 @@
 #define GTUNE_USE_SYSTEMD        (1<<10)
 
 #define GTUNE_BUSY_POLLING       (1<<11)
-#define GTUNE_LISTENER_MQ        (1<<12)
+/* unused: (1<<12) */
 #define GTUNE_SET_DUMPABLE       (1<<13)
 #define GTUNE_USE_EVPORTS        (1<<14)
 #define GTUNE_STRICT_LIMITS      (1<<15)
@@ -81,6 +81,9 @@
 #define GTUNE_QUIC_SOCK_PER_CONN (1<<24)
 #define GTUNE_NO_QUIC            (1<<25)
 #define GTUNE_USE_FAST_FWD       (1<<26)
+#define GTUNE_LISTENER_MQ_FAIR   (1<<27)
+#define GTUNE_LISTENER_MQ_OPT    (1<<28)
+#define GTUNE_LISTENER_MQ_ANY    (GTUNE_LISTENER_MQ_FAIR | GTUNE_LISTENER_MQ_OPT)
 
 /* SSL server verify mode */
 enum {
index fd43c963a26a6a6707b2e20d06109c36834c5e41..53061131995445241b5fe0a8d072f02c4a904ae5 100644 (file)
@@ -187,7 +187,7 @@ struct global global = {
                 }
        },
        .tune = {
-               .options = GTUNE_LISTENER_MQ,
+               .options = GTUNE_LISTENER_MQ_OPT,
                .bufsize = (BUFSIZE + 2*sizeof(void *) - 1) & -(2*sizeof(void *)),
                .maxrewrite = MAXREWRITE,
                .reserved_bufs = RESERVED_BUFS,
index d6e58ce69a3077d11bf36aafd4c875f93ef99247..e441eff2efb716a059719036cc8b25545338c6a9 100644 (file)
@@ -1099,7 +1099,7 @@ void listener_accept(struct listener *l)
 
 #if defined(USE_THREAD)
                mask = l->rx.bind_thread & _HA_ATOMIC_LOAD(&tg->threads_enabled);
-               if (atleast2(mask) && (global.tune.options & GTUNE_LISTENER_MQ) && !stopping) {
+               if (atleast2(mask) && (global.tune.options & GTUNE_LISTENER_MQ_ANY) && !stopping) {
                        struct accept_queue_ring *ring;
                        unsigned int t, t0, t1, t2;
                        int base = tg->base;
@@ -1140,6 +1140,14 @@ void listener_accept(struct listener *l)
                                        t1 += my_ffsl(m1) - 1;
                                }
 
+                               /* if running in round-robin mode ("fair"), we don't need
+                                * to go further.
+                                */
+                               if ((global.tune.options & GTUNE_LISTENER_MQ_ANY) == GTUNE_LISTENER_MQ_FAIR) {
+                                       t = t1;
+                                       goto updt_t1;
+                               }
+
                                if (unlikely(!(m2 & (1UL << t2)) || t1 == t2)) {
                                        /* highest bit not set */
                                        if (!m2)
@@ -1184,6 +1192,7 @@ void listener_accept(struct listener *l)
                                }
                                else {
                                        t = t1;
+                               updt_t1:
                                        t1++;
                                        if (t1 >= LONGBITS)
                                                t1 = 0;
@@ -1898,7 +1907,7 @@ static int bind_parse_thread(char **args, int cur_arg, struct proxy *px, struct
        return 0;
 }
 
-/* config parser for global "tune.listener.multi-queue", accepts "on" or "off" */
+/* config parser for global "tune.listener.multi-queue", accepts "on", "fair" or "off" */
 static int cfg_parse_tune_listener_mq(char **args, int section_type, struct proxy *curpx,
                                       const struct proxy *defpx, const char *file, int line,
                                       char **err)
@@ -1907,11 +1916,13 @@ static int cfg_parse_tune_listener_mq(char **args, int section_type, struct prox
                return -1;
 
        if (strcmp(args[1], "on") == 0)
-               global.tune.options |= GTUNE_LISTENER_MQ;
+               global.tune.options = (global.tune.options & ~GTUNE_LISTENER_MQ_ANY) | GTUNE_LISTENER_MQ_OPT;
+       else if (strcmp(args[1], "fair") == 0)
+               global.tune.options = (global.tune.options & ~GTUNE_LISTENER_MQ_ANY) | GTUNE_LISTENER_MQ_FAIR;
        else if (strcmp(args[1], "off") == 0)
-               global.tune.options &= ~GTUNE_LISTENER_MQ;
+               global.tune.options &= ~GTUNE_LISTENER_MQ_ANY;
        else {
-               memprintf(err, "'%s' expects either 'on' or 'off' but got '%s'.", args[0], args[1]);
+               memprintf(err, "'%s' expects either 'on', 'fair', or 'off' but got '%s'.", args[0], args[1]);
                return -1;
        }
        return 0;