From: Christopher Faulet Date: Wed, 28 Aug 2024 08:02:13 +0000 (+0200) Subject: MEDIUM: stick-table: Add support of a factor for IN/OUT bytes rates X-Git-Tag: v3.1-dev7~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a7f6b0ac03618b8edea895bd58df27501ce710aa;p=thirdparty%2Fhaproxy.git MEDIUM: stick-table: Add support of a factor for IN/OUT bytes rates Add a factor parameter to stick-tables, called "brates-factor", that is applied to in/out bytes rates to work around the 32-bits limit of the frequency counters. Thanks to this factor, it is possible to have bytes rates beyond the 4GB. Instead of counting each bytes, we count blocks of bytes. Among other things, it will be useful for the bwlim filter, to be able to configure shared limit exceeding the 4GB/s. For now, this parameter must be in the range ]0-1024]. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 15bf24e342..11d6879288 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -12446,7 +12446,7 @@ stick store-request [table ] [{if | unless} ] stick-table type {ip | integer | string [len ] | binary [len ]} size [expire ] [nopurge] [peers ] [srvkey ] - [write-to ] [store ]* + [write-to ] [store ]* [brates-factor ] Configure the stickiness table for the current section May be used in the following contexts: tcp, http @@ -12568,6 +12568,13 @@ stick-table type {ip | integer | string [len ] | binary [len ]} the type between parenthesis. See below for the supported data types and their arguments. + is used to define a factor to be applied on in/out bytes rate. + Instead of counting each bytes, blocks of bytes are counted. + Internally, rates are defined on 32-bits counters. By using this + parameter, it is possible to have rates exceeding the 4G on the + defined period. The factor must be greater than 0 and lower or + equal to 1024. + The data types that can be stored with an entry are the following : - server_id : this is an integer which holds the numeric ID of the server a request was assigned to. It is used by the "stick match", "stick store", diff --git a/include/haproxy/stick_table-t.h b/include/haproxy/stick_table-t.h index 4b984396e9..3aa55e378e 100644 --- a/include/haproxy/stick_table-t.h +++ b/include/haproxy/stick_table-t.h @@ -184,6 +184,7 @@ struct stktable { int data_size; /* the size of the data that is prepended *before* stksess */ int data_ofs[STKTABLE_DATA_TYPES]; /* negative offsets of present data types, or 0 if absent */ unsigned int data_nbelem[STKTABLE_DATA_TYPES]; /* to store nb_elem in case of array types */ + unsigned int brates_factor; /* Factor used for IN/OUT bytes rates */ union { int i; unsigned int u; diff --git a/include/haproxy/stick_table.h b/include/haproxy/stick_table.h index 4132af1293..14b3e2d8ff 100644 --- a/include/haproxy/stick_table.h +++ b/include/haproxy/stick_table.h @@ -395,7 +395,8 @@ static inline int stkctr_inc_bytes_in_ctr(struct stkctr *stkctr, unsigned long l ptr2 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_BYTES_IN_RATE); if (ptr2) update_freq_ctr_period(&stktable_data_cast(ptr2, std_t_frqp), - stkctr->table->data_arg[STKTABLE_DT_BYTES_IN_RATE].u, bytes); + stkctr->table->data_arg[STKTABLE_DT_BYTES_IN_RATE].u, + div64_32(bytes + stkctr->table->brates_factor - 1, stkctr->table->brates_factor)); HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock); @@ -426,7 +427,8 @@ static inline int stkctr_inc_bytes_out_ctr(struct stkctr *stkctr, unsigned long ptr2 = stktable_data_ptr(stkctr->table, ts, STKTABLE_DT_BYTES_OUT_RATE); if (ptr2) update_freq_ctr_period(&stktable_data_cast(ptr2, std_t_frqp), - stkctr->table->data_arg[STKTABLE_DT_BYTES_OUT_RATE].u, bytes); + stkctr->table->data_arg[STKTABLE_DT_BYTES_OUT_RATE].u, + div64_32(bytes + stkctr->table->brates_factor - 1, stkctr->table->brates_factor)); HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock); diff --git a/src/flt_bwlim.c b/src/flt_bwlim.c index 11eb2484a9..1d07598033 100644 --- a/src/flt_bwlim.c +++ b/src/flt_bwlim.c @@ -78,7 +78,8 @@ static int bwlim_apply_limit(struct filter *filter, struct channel *chn, unsigne struct bwlim_config *conf = FLT_CONF(filter); struct bwlim_state *st = filter->ctx; struct freq_ctr *bytes_rate; - unsigned int period, limit, remain, tokens, users; + uint64_t remain; + unsigned int period, limit, tokens, users, factor; unsigned int wait = 0; int overshoot, ret = 0; @@ -110,6 +111,7 @@ static int bwlim_apply_limit(struct filter *filter, struct channel *chn, unsigne period = conf->table.t->data_arg[type].u; limit = conf->limit; users = st->ts->ref_cnt; + factor = conf->table.t->brates_factor; } else { /* On per-stream mode, the freq-counter is private to the @@ -121,6 +123,7 @@ static int bwlim_apply_limit(struct filter *filter, struct channel *chn, unsigne period = (st->period ? st->period : conf->period); limit = (st->limit ? st->limit : conf->limit); users = 1; + factor = 1; } /* Be sure the current rate does not exceed the limit over the current @@ -143,7 +146,7 @@ static int bwlim_apply_limit(struct filter *filter, struct channel *chn, unsigne } /* Get the allowed quota per user. */ - remain = freq_ctr_remain_period(bytes_rate, period, limit, 0); + remain = (uint64_t)freq_ctr_remain_period(bytes_rate, period, limit, 0) * factor; tokens = div64_32((uint64_t)(remain + users - 1), users); if (tokens < len) { @@ -159,16 +162,16 @@ static int bwlim_apply_limit(struct filter *filter, struct channel *chn, unsigne : conf->min_size; if (ret <= remain) - wait = div64_32((uint64_t)(ret - tokens) * period * users + limit - 1, limit); + wait = div64_32((uint64_t)(ret - tokens) * period * users + limit * factor - 1, limit * factor); else - ret = (limit < ret) ? remain : 0; + ret = (limit * factor < ret) ? remain : 0; } } /* At the end, update the freq-counter and compute the waiting time if * the stream is limited */ - update_freq_ctr_period(bytes_rate, period, ret); + update_freq_ctr_period(bytes_rate, period, div64_32((uint64_t)ret + factor -1, factor)); if (ret < len) { wait += next_event_delay_period(bytes_rate, period, limit, MIN(len - ret, conf->min_size * users)); st->exp = tick_add(now_ms, (wait ? wait : 1)); diff --git a/src/stick_table.c b/src/stick_table.c index c6e5cf0c78..f69146d1d1 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -1184,6 +1184,7 @@ int parse_stick_table(const char *file, int linenum, char **args, t->conf.file = file; t->conf.line = linenum; t->write_to.name = NULL; + t->brates_factor = 1; while (*args[idx]) { const char *err; @@ -1388,6 +1389,28 @@ int parse_stick_table(const char *file, int linenum, char **args, t->write_to.name = strdup(write_to); idx++; } + else if (strcmp(args[idx], "brates-factor") == 0) { + idx++; + if (!*(args[idx])) { + ha_alert("parsing [%s:%d] : %s: missing argument after '%s'.\n", + file, linenum, args[0], args[idx-1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + if ((err = parse_size_err(args[idx], &t->brates_factor))) { + ha_alert("parsing [%s:%d] : %s: unexpected character '%c' in argument of '%s'.\n", + file, linenum, args[0], *err, args[idx-1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + if (t->brates_factor == 0 || t->brates_factor > 1024) { + ha_alert("parsing [%s:%d] : %s: argument '%s' must be greater than 0 and lower or equal than 1024.\n", + file, linenum, args[0], args[idx-1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + idx++; + } else { ha_alert("parsing [%s:%d] : %s: unknown argument '%s'.\n", file, linenum, args[0], args[idx]); @@ -1677,8 +1700,8 @@ static int sample_conv_table_bytes_in_rate(const struct arg *arg_p, struct sampl ptr = stktable_data_ptr(t, ts, STKTABLE_DT_BYTES_IN_RATE); if (ptr) - smp->data.u.sint = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), - t->data_arg[STKTABLE_DT_BYTES_IN_RATE].u); + smp->data.u.sint = (uint64_t)read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), + t->data_arg[STKTABLE_DT_BYTES_IN_RATE].u) * t->brates_factor; stktable_release(t, ts); return !!ptr; @@ -1899,8 +1922,9 @@ static int sample_conv_table_bytes_out_rate(const struct arg *arg_p, struct samp ptr = stktable_data_ptr(t, ts, STKTABLE_DT_BYTES_OUT_RATE); if (ptr) - smp->data.u.sint = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), - t->data_arg[STKTABLE_DT_BYTES_OUT_RATE].u); + smp->data.u.sint = (uint64_t)read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), + t->data_arg[STKTABLE_DT_BYTES_OUT_RATE].u) * t->brates_factor; + stktable_release(t, ts); return !!ptr; @@ -4918,8 +4942,8 @@ smp_fetch_sc_bytes_in_rate(const struct arg *args, struct sample *smp, const cha HA_RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock); - smp->data.u.sint = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), - stkctr->table->data_arg[STKTABLE_DT_BYTES_IN_RATE].u); + smp->data.u.sint = (uint64_t)read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), + stkctr->table->data_arg[STKTABLE_DT_BYTES_IN_RATE].u) * stkctr->table->brates_factor; HA_RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock); @@ -4997,8 +5021,8 @@ smp_fetch_sc_bytes_out_rate(const struct arg *args, struct sample *smp, const ch HA_RWLOCK_RDLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock); - smp->data.u.sint = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), - stkctr->table->data_arg[STKTABLE_DT_BYTES_OUT_RATE].u); + smp->data.u.sint = (uint64_t)read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), + stkctr->table->data_arg[STKTABLE_DT_BYTES_OUT_RATE].u) * stkctr->table->brates_factor; HA_RWLOCK_RDUNLOCK(STK_SESS_LOCK, &stkctr_entry(stkctr)->lock); @@ -5110,6 +5134,7 @@ static int table_dump_entry_to_buffer(struct buffer *msg, for (dt = 0; dt < STKTABLE_DATA_TYPES; dt++) { void *ptr; + long long data; if (t->data_ofs[dt] == 0) continue; @@ -5151,9 +5176,11 @@ static int table_dump_entry_to_buffer(struct buffer *msg, chunk_appendf(msg, "%llu", stktable_data_cast(ptr, std_t_ull)); break; case STD_T_FRQP: - chunk_appendf(msg, "%u", - read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), - t->data_arg[dt].u)); + data = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), + t->data_arg[dt].u); + if (dt == STKTABLE_DT_BYTES_IN_RATE || dt == STKTABLE_DT_BYTES_OUT_RATE) + data *= t->brates_factor; + chunk_appendf(msg, "%llu", data); break; } ptr = stktable_data_ptr_idx(t, entry, dt, ++idx); @@ -5177,9 +5204,11 @@ static int table_dump_entry_to_buffer(struct buffer *msg, chunk_appendf(msg, "%llu", stktable_data_cast(ptr, std_t_ull)); break; case STD_T_FRQP: - chunk_appendf(msg, "%u", - read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), - t->data_arg[dt].u)); + data = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), + t->data_arg[dt].u); + if (dt == STKTABLE_DT_BYTES_IN_RATE || dt == STKTABLE_DT_BYTES_OUT_RATE) + data *= t->brates_factor; + chunk_appendf(msg, "%llu", data); break; case STD_T_DICT: { struct dict_entry *de; @@ -5617,6 +5646,8 @@ static int cli_io_handler_table(struct appctx *appctx) case STD_T_FRQP: data = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp), ctx->t->data_arg[dt].u); + if (dt == STKTABLE_DT_BYTES_IN_RATE || dt == STKTABLE_DT_BYTES_OUT_RATE) + data *= ctx->t->brates_factor; break; }