mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-05-13 09:16:09 +02:00
MINOR: connection: add a function to calculate elastic streams limit
This adds a new tune.streams-elasticity parameter. This parameter indicates, as a percentage, the average number of streams per connection at full load. It is used to calculate limits of the number of streams to advertise on new connections. 0 means that no such limit is set. When a limit is set, the new function conn_calc_max_streams() determines the optimal number of streams to allow on a connection. It will assign at least the ratio of streams left to connections left, and at least a fair share of what's left times the number of desired streams. It will always ensure that each connection gets at least 1 stream, and everything beyond this will be evenly distributed. For now the function is not used.
This commit is contained in:
parent
7f17512d18
commit
dd36c84a7b
@ -2001,6 +2001,7 @@ The following keywords are supported in the "global" section :
|
||||
- tune.sndbuf.client
|
||||
- tune.sndbuf.frontend
|
||||
- tune.sndbuf.server
|
||||
- tune.streams-elasticity
|
||||
- tune.stick-counters
|
||||
- tune.ssl.cachesize
|
||||
- tune.ssl.capture-buffer-size
|
||||
@ -5689,6 +5690,39 @@ tune.ssl.ssl-ctx-cache-size <number>
|
||||
dynamically is expensive, they are cached. The default cache size is set to
|
||||
1000 entries.
|
||||
|
||||
tune.streams-elasticity <number>
|
||||
Defines a target percentage of streams per frontend connection relative to
|
||||
the maximum number of concurrent connections (maxconn) when all connections
|
||||
are established. This metric applies to multiplexed protocols like HTTP/2 or
|
||||
QUIC, where each connection may receive multiple streams. At least one is
|
||||
always guaranteed, so the percentage must be at least 100%. During connection
|
||||
setup, HAProxy dynamically advertises additional streams up to the configured
|
||||
limit, maintaining the target ratio. At connection establishment, every
|
||||
frontend connection receives at least one stream; extra streams are assigned
|
||||
based on the target percentage and configured stream limits. This ensures
|
||||
efficient stream allocation under varying load conditions (more streams at
|
||||
low loads, fewer at high loads).
|
||||
|
||||
Highly dynamic sites with many objects per page benefit from high ratios,
|
||||
enabling many streams per connection. Sites using fewer streams on average
|
||||
(WebSocket, application code) may prefer small ratios closer to 120 or 150
|
||||
(20 to 50% more streams than connections) preventing excessive stream counts
|
||||
under sustained loads.
|
||||
|
||||
The default value is 0, meaning no enforcement at this level, so only H2 and
|
||||
QUIC configurations apply (with the default setting of 100 streams per
|
||||
connection, this corresponds to 10000%). This remains the recommended setting
|
||||
for small deployments (maxconn around a thousand). Moderately sized setups
|
||||
(few thousands to tens of thousands connections) typically set the ratio
|
||||
between 1000 and 5000, allowing 10 to 50 streams per connection at full load.
|
||||
Large-scale deployments (hundreds of thousands to millions connections) might
|
||||
use lower values (120 to 200) to support 1.2 to 2 streams per connection on
|
||||
average at full load.
|
||||
|
||||
Monitoring the total number of active streams on backends, including queues,
|
||||
provides a practical indicator of a sustainable target load and helps avoid
|
||||
over-provisioning.
|
||||
|
||||
tune.stick-counters <number>
|
||||
Sets the number of stick-counters that may be tracked at the same time by a
|
||||
connection or a request via "track-sc*" actions in "tcp-request" or
|
||||
|
||||
@ -496,6 +496,64 @@ static inline int conn_install_mux(struct connection *conn, const struct mux_ops
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Calculates the approximate number of streams permitted for an already
|
||||
* established frontend connection based on the number of active connections
|
||||
* (including this one), the number of already committed streams in the current
|
||||
* thread group, the limit, and the desired limit (a ratio of which will be
|
||||
* applied as the budget permits). May return 0 for no limit. The minimum value
|
||||
* when a limit is set will be 1 as a minimum.
|
||||
*/
|
||||
static inline uint conn_calc_max_streams(uint desired)
|
||||
{
|
||||
uint per_conn_left;
|
||||
uint avg_per_conn;
|
||||
uint conn_curr;
|
||||
int conn_left;
|
||||
uint extra;
|
||||
uint curr;
|
||||
|
||||
/* check for infinite */
|
||||
if (!global.tune.streams_elasticity)
|
||||
return 0;
|
||||
|
||||
/* check for none (0% overcommit) */
|
||||
if (global.tune.streams_elasticity == 100)
|
||||
return 1;
|
||||
|
||||
if (desired <= 1)
|
||||
return 1;
|
||||
|
||||
conn_curr = _HA_ATOMIC_LOAD(&actconn) - 1;
|
||||
conn_left = global.hardmaxconn - conn_curr;
|
||||
if (conn_left <= 0)
|
||||
return 1;
|
||||
|
||||
/* the limit is per process, we're working per group. Since we're
|
||||
* counting extra streams max, we subtract 100% from elasticity.
|
||||
*/
|
||||
extra = (((ullong)global.hardmaxconn * (global.tune.streams_elasticity - 100) / 100));
|
||||
curr = _HA_ATOMIC_LOAD(&tg_ctx->committed_extra_streams) * global.nbtgroups;
|
||||
if (curr >= extra)
|
||||
return 1;
|
||||
|
||||
/* this is the average per conn left that we can allocate */
|
||||
per_conn_left = ((extra - curr) + conn_left - 1) / conn_left;
|
||||
|
||||
/* OK so we know we can still allocate (extra - curr) streams per
|
||||
* tgroup, that will be shared across conn_left connections, but ought
|
||||
* to be fairly shared between all conn_curr ones. This allows to
|
||||
* provide at least up to <desired> as long as we leave enough for all
|
||||
* remaining connections left.
|
||||
*/
|
||||
avg_per_conn = ((ullong)(extra - curr) * (desired - 1)) / extra;
|
||||
|
||||
/* both values are permitted since they respect the global limit,
|
||||
* so let's deliver the best option to better serve first conns
|
||||
* so that the limit degrades smoothly with the number of conns.
|
||||
*/
|
||||
return 1 + MAX(per_conn_left, avg_per_conn);
|
||||
}
|
||||
|
||||
/* Retrieves any valid stream connector from this connection, preferably the first
|
||||
* valid one. The purpose is to be able to figure one other end of a private
|
||||
* connection for purposes like source binding or proxy protocol header
|
||||
|
||||
@ -216,6 +216,7 @@ struct global {
|
||||
uint max_checks_per_thread; /* if >0, no more than this concurrent checks per thread */
|
||||
uint ring_queues; /* if >0, #ring queues, otherwise equals #thread groups */
|
||||
uint cli_max_payload_sz; /* The max payload size for the CLI */
|
||||
int streams_elasticity; /* percent of advertised streams to connection; 0=no limit */
|
||||
enum threadgroup_takeover tg_takeover; /* Policy for threadgroup takeover */
|
||||
} tune;
|
||||
struct {
|
||||
|
||||
@ -1444,6 +1444,16 @@ static int cfg_parse_global_tune_opts(char **args, int section_type,
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[0], "tune.streams-elasticity") == 0) {
|
||||
char *stop;
|
||||
|
||||
global.tune.streams_elasticity = strtol(args[1], &stop, 10);
|
||||
if (!*args[1] || *stop ||
|
||||
(global.tune.streams_elasticity && global.tune.streams_elasticity < 100)) {
|
||||
memprintf(err, "'%s' expects 0 or a positive percentage value of 100 or above", args[0]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (strcmp(args[0], "tune.takeover-other-tg-connections") == 0) {
|
||||
if (*(args[1]) == 0) {
|
||||
memprintf(err, "'%s' expects 'none', 'restricted', or 'full'", args[0]);
|
||||
@ -1903,6 +1913,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
|
||||
{ CFG_GLOBAL, "tune.runqueue-depth", cfg_parse_global_tune_opts },
|
||||
{ CFG_GLOBAL, "tune.sndbuf.client", cfg_parse_global_tune_opts },
|
||||
{ CFG_GLOBAL, "tune.sndbuf.server", cfg_parse_global_tune_opts },
|
||||
{ CFG_GLOBAL, "tune.streams-elasticity", cfg_parse_global_tune_opts },
|
||||
{ CFG_GLOBAL, "tune.takeover-other-tg-connections", cfg_parse_global_tune_opts },
|
||||
{ CFG_GLOBAL, "unsetenv", cfg_parse_global_env_opts, KWF_DISCOVERY },
|
||||
{ CFG_GLOBAL, "zero-warning", cfg_parse_global_mode, KWF_DISCOVERY },
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user