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].
This commit is contained in:
Christopher Faulet 2024-08-28 10:02:13 +02:00
parent db13df3d6e
commit a7f6b0ac03
5 changed files with 66 additions and 22 deletions

View File

@ -12446,7 +12446,7 @@ stick store-request <pattern> [table <table>] [{if | unless} <condition>]
stick-table type {ip | integer | string [len <length>] | binary [len <length>]}
size <size> [expire <expire>] [nopurge] [peers <peersect>] [srvkey <srvkey>]
[write-to <wtable>] [store <data_type>]*
[write-to <wtable>] [store <data_type>]* [brates-factor <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 <length>] | binary [len <length>]}
the type between parenthesis. See below for the supported data
types and their arguments.
<factor> 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",

View File

@ -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;

View File

@ -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);

View File

@ -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));

View File

@ -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;
}