]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: mux-h2: make the initial window size configurable per side
authorWilly Tarreau <w@1wt.eu>
Mon, 17 Apr 2023 13:04:34 +0000 (15:04 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 18 Apr 2023 13:58:55 +0000 (15:58 +0200)
For a long time the initial window size (per-stream size) was set once
for both directions, frontend and backend, resulting in a tradeoff between
upload speed and download fairness. This commit allows it to be configured
separately for each side. The older settings remains the fallback choice
when other ones are not set.

doc/configuration.txt
src/mux_h2.c

index 3adab34873380b74d523d02f2732db84cb051a23..082102b9e2453039ce7a66e74b5cc7c82343a2cb 100644 (file)
@@ -2885,6 +2885,28 @@ tune.fd.edge-triggered { on | off }  [ EXPERIMENTAL ]
   certain scenarios. This is still experimental, it may result in frozen
   connections if bugs are still present, and is disabled by default.
 
+tune.h2.be.initial-window-size <number>
+  Sets the HTTP/2 initial window size for outgoing connections, which is the
+  number of bytes the server can respond before waiting for an acknowledgment
+  from HAProxy. This setting only affects payload contents, not headers. When
+  not set, the common default value set by tune.h2.initial-window-size applies.
+  It can make sense to slightly increase this value to allow faster downloads
+  or to reduce CPU usage on the servers, at the expense of creating unfairness
+  between clients. It doesn't affect resource usage.
+  See also: tune.h2.initial-window-size.
+
+tune.h2.fe.initial-window-size <number>
+  Sets the HTTP/2 initial window size for incoming connections, which is the
+  number of bytes the client can upload before waiting for an acknowledgment
+  from HAProxy. This setting only affects payload contents (i.e. the body of
+  POST requests), not headers. When not set, the common default value set by
+  tune.h2.initial-window-size applies. It can make sense to increase this value
+  to allow faster uploads. The default value of 65536 allows up to 5 Mbps of
+  bandwidth per client over a 100 ms ping time, and 500 Mbps for 1 ms ping
+  time. It doesn't affect resource usage. Using too large values may cause
+  clients to experience a lack of responsiveness if pages are accessed in
+  parallel to large uploads. See also: tune.h2.initial-window-size.
+
 tune.h2.header-table-size <number>
   Sets the HTTP/2 dynamic header table size. It defaults to 4096 bytes and
   cannot be larger than 65536 bytes. A larger value may help certain clients
@@ -2893,14 +2915,16 @@ tune.h2.header-table-size <number>
   change it.
 
 tune.h2.initial-window-size <number>
-  Sets the HTTP/2 initial window size, which is the number of bytes the client
-  can upload before waiting for an acknowledgment from HAProxy. This setting
-  only affects payload contents (i.e. the body of POST requests), not headers.
-  The default value is 65536, which roughly allows up to 5 Mbps of upload
-  bandwidth per client over a network showing a 100 ms ping time, or 500 Mbps
-  over a 1-ms local network. It can make sense to increase this value to allow
-  faster uploads, or to reduce it to increase fairness when dealing with many
-  clients. It doesn't affect resource usage.
+  Sets the default value for the HTTP/2 initial window size, on both incoming
+  and outgoing connections. This value is used for incoming connections when
+  tune.h2.fe.initial-window-size is not set, and by outgoing connections when
+  tune.h2.be.initial-window-size is not set. The default value is 65536, which
+  for uploads roughly allows up to 5 Mbps of bandwidth per client over a
+  network showing a 100 ms ping time, or 500 Mbps over a 1-ms local network.
+  Given that changing the default value will both increase upload speeds and
+  cause more unfairness between clients on downloads, it is recommended to
+  instead use the side-specific settings tune.h2.fe.initial-window-size and
+  tune.h2.be.initial-window-size.
 
 tune.h2.max-concurrent-streams <number>
   Sets the HTTP/2 maximum number of concurrent streams per connection (ie the
index 2f06db8dcb8696004d8aa500334d4cddd92c7ca5..3510c3b24d0e5aa35deced1919b8201da233c614 100644 (file)
@@ -403,7 +403,9 @@ DECLARE_STATIC_POOL(pool_head_h2s, "h2s", sizeof(struct h2s));
 
 /* a few settings from the global section */
 static int h2_settings_header_table_size      =  4096; /* initial value */
-static int h2_settings_initial_window_size    = 65536; /* initial value */
+static int h2_settings_initial_window_size    = 65536; /* default initial value */
+static int h2_be_settings_initial_window_size =     0; /* backend's default initial value */
+static int h2_fe_settings_initial_window_size =     0; /* frontend's default initial value */
 static unsigned int h2_settings_max_concurrent_streams = 100;
 static int h2_settings_max_frame_size         = 0;     /* unset */
 
@@ -1641,6 +1643,7 @@ static int h2c_send_settings(struct h2c *h2c)
        struct buffer *res;
        char buf_data[100]; // enough for 15 settings
        struct buffer buf;
+       int iws;
        int mfs;
        int ret = 0;
 
@@ -1670,10 +1673,15 @@ static int h2c_send_settings(struct h2c *h2c)
                chunk_memcat(&buf, str, 6);
        }
 
-       if (h2_settings_initial_window_size != 65535) {
+       iws = (h2c->flags & H2_CF_IS_BACK) ?
+             h2_be_settings_initial_window_size:
+             h2_fe_settings_initial_window_size;
+       iws = iws ? iws : h2_settings_initial_window_size;
+
+       if (iws != 65535) {
                char str[6] = "\x00\x04"; /* initial_window_size */
 
-               write_n32(str + 2, h2_settings_initial_window_size);
+               write_n32(str + 2, iws);
                chunk_memcat(&buf, str, 6);
        }
 
@@ -6893,16 +6901,23 @@ static int h2_parse_header_table_size(char **args, int section_type, struct prox
        return 0;
 }
 
-/* config parser for global "tune.h2.initial-window-size" */
+/* config parser for global "tune.h2.{be.,fe.,}initial-window-size" */
 static int h2_parse_initial_window_size(char **args, int section_type, struct proxy *curpx,
                                         const struct proxy *defpx, const char *file, int line,
                                         char **err)
 {
+       int *vptr;
+
        if (too_many_args(1, args, err, NULL))
                return -1;
 
-       h2_settings_initial_window_size = atoi(args[1]);
-       if (h2_settings_initial_window_size < 0) {
+       /* backend/frontend/default */
+       vptr = (args[0][8] == 'b') ? &h2_be_settings_initial_window_size :
+              (args[0][8] == 'f') ? &h2_fe_settings_initial_window_size :
+              &h2_settings_initial_window_size;
+
+       *vptr = atoi(args[1]);
+       if (*vptr < 0) {
                memprintf(err, "'%s' expects a positive numeric value.", args[0]);
                return -1;
        }
@@ -6977,6 +6992,8 @@ INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_h2);
 
 /* config keyword parsers */
 static struct cfg_kw_list cfg_kws = {ILH, {
+       { CFG_GLOBAL, "tune.h2.be.initial-window-size", h2_parse_initial_window_size    },
+       { CFG_GLOBAL, "tune.h2.fe.initial-window-size", h2_parse_initial_window_size    },
        { CFG_GLOBAL, "tune.h2.header-table-size",      h2_parse_header_table_size      },
        { CFG_GLOBAL, "tune.h2.initial-window-size",    h2_parse_initial_window_size    },
        { CFG_GLOBAL, "tune.h2.max-concurrent-streams", h2_parse_max_concurrent_streams },