]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: stick-table: Add support of a factor for IN/OUT bytes rates
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 28 Aug 2024 08:02:13 +0000 (10:02 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 2 Sep 2024 13:50:25 +0000 (15:50 +0200)
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].

doc/configuration.txt
include/haproxy/stick_table-t.h
include/haproxy/stick_table.h
src/flt_bwlim.c
src/stick_table.c

index 15bf24e3429829796b8fa760c583ca117c32d207..11d68792883edc68a7713a58556d8867b5b40ea3 100644 (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",
index 4b984396e9e86ff164887819e1e70535ae63e94d..3aa55e378e7100de46f13d9f87655283b9fb4bfa 100644 (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;
index 4132af1293e43e0ec67b0ce575d0a06d6d60e650..14b3e2d8ff3808122be50cbe0c35aafc5448352c 100644 (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);
 
 
index 11eb2484a9e2aa2bb5f22cc2611b69454ff494ee..1d07598033a48facaab8c0b4773d39d298ce5eb4 100644 (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));
index c6e5cf0c78709d199dee034aeab686ab13ab3a03..f69146d1d149abe7b3c722cedf9b087d08836a53 100644 (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;
                                        }