]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
stats: Add support for exponential stats group-by fxn
authorJosef 'Jeff' Sipek <jeff.sipek@open-xchange.com>
Thu, 5 Mar 2020 12:20:53 +0000 (07:20 -0500)
committerjeff.sipek <jeff.sipek@open-xchange.com>
Fri, 13 Mar 2020 08:25:17 +0000 (08:25 +0000)
One can specify the quantization parameters in the config file as:

    <field>:exponential:<min mag>:<max mag>:<base>

Currently, only base 2 and base 10 are supported.

For example:

    group_by = bytes_out:exponential:1:4:10

Which will quantize the bytes_out values into the buckets: (-inf, 10],
(10,100], (100,1000], (1000, 10000], (10000, +inf).

src/config/Makefile.am
src/stats/Makefile.am
src/stats/stats-settings.c
src/stats/test-stats-metrics.c

index 149d7e6025fba2e70d0bb48124662a45fc853379..ba39c146acbfe4adf34ee11b8077584405e95064 100644 (file)
@@ -23,14 +23,16 @@ AM_CPPFLAGS = \
 config_LDADD = \
        $(LIBDOVECOT) \
        $(RAND_LIBS) \
-       $(BINARY_LDFLAGS)
+       $(BINARY_LDFLAGS) \
+       -lm
 
 config_DEPENDENCIES = $(LIBDOVECOT_DEPS)
 
 doveconf_LDADD = \
        $(LIBDOVECOT) \
        $(RAND_LIBS) \
-       $(BINARY_LDFLAGS)
+       $(BINARY_LDFLAGS) \
+       -lm
 
 doveconf_DEPENDENCIES = $(LIBDOVECOT_DEPS)
 
index b036b7a72b03dc049a9a34c4d912552f6851646a..edc9c55ea649eceaa624194570a9fed3c7277980 100644 (file)
@@ -17,7 +17,8 @@ stats_LDADD = \
        $(noinst_LTLIBRARIES) \
        $(LIBDOVECOT) \
        $(DOVECOT_SSL_LIBS) \
-       $(BINARY_LDFLAGS)
+       $(BINARY_LDFLAGS) \
+       -lm
 
 stats_DEPENDENCIES = \
        $(noinst_LTLIBRARIES) \
@@ -54,7 +55,8 @@ test_libs = \
        $(noinst_LTLIBRARIES) \
        $(DOVECOT_SSL_LIBS) \
        $(LIBDOVECOT) \
-       $(BINARY_LDFLAGS)
+       $(BINARY_LDFLAGS) \
+       -lm
 
 test_deps = \
        $(noinst_LTLIBRARIES) \
index ed6464f524a93fd0e2eb238530912b3ce5dfb0d8..3e8e045851bce2a1efacf3743b561a4162ee3454 100644 (file)
@@ -7,6 +7,10 @@
 #include "stats-settings.h"
 #include "array.h"
 
+/* <settings checks> */
+#include <math.h>
+/* </settings checks> */
+
 static bool stats_metric_settings_check(void *_set, pool_t pool, const char **error_r);
 static bool stats_exporter_settings_check(void *_set, pool_t pool, const char **error_r);
 static bool stats_settings_check(void *_set, pool_t pool, const char **error_r);
@@ -318,6 +322,52 @@ static bool parse_metric_group_by_common(const char *func,
        return TRUE;
 }
 
+static bool parse_metric_group_by_exp(pool_t pool, struct stats_metric_settings_group_by *group_by,
+                                     const char *const *params, const char **error_r)
+{
+       intmax_t min, max, base;
+
+       if (!parse_metric_group_by_common("exponential", params, &min, &max, &base, error_r))
+               return FALSE;
+
+       if ((base != 2) && (base != 10)) {
+               *error_r = t_strdup_printf("group_by 'exponential' aggregate function "
+                                          "base must be one of: 2, 10 (base=%ju)",
+                                          base);
+               return FALSE;
+       }
+
+       group_by->func = STATS_METRIC_GROUPBY_QUANTIZED;
+
+       /*
+        * Allocate the bucket range array and fill it in
+        *
+        * The first bucket is special - it contains everything less than or
+        * equal to 'base^min'.  The last bucket is also special - it
+        * contains everything greater than 'base^max'.
+        *
+        * The second bucket begins at 'base^min + 1', the third bucket
+        * begins at 'base^(min + 1) + 1', and so on.
+        */
+       group_by->num_ranges = max - min + 2;
+       group_by->ranges = p_new(pool, struct stats_metric_settings_bucket_range,
+                                group_by->num_ranges);
+
+       /* set up min & max buckets */
+       group_by->ranges[0].min = INTMAX_MIN;
+       group_by->ranges[0].max = pow(base, min);
+       group_by->ranges[group_by->num_ranges - 1].min = pow(base, max);
+       group_by->ranges[group_by->num_ranges - 1].max = INTMAX_MAX;
+
+       /* remaining buckets */
+       for (unsigned int i = 1; i < group_by->num_ranges - 1; i++) {
+               group_by->ranges[i].min = pow(base, min + (i - 1));
+               group_by->ranges[i].max = pow(base, min + i);
+       }
+
+       return TRUE;
+}
+
 static bool parse_metric_group_by_lin(pool_t pool, struct stats_metric_settings_group_by *group_by,
                                      const char *const *params, const char **error_r)
 {
@@ -395,6 +445,10 @@ static bool parse_metric_group_by(struct stats_metric_settings *set,
                                           "does not take any args";
                                return FALSE;
                        }
+               } else if (strcmp(params[1], "exponential") == 0) {
+                       /* <field>:exponential:<min mag>:<max mag>:<base> */
+                       if (!parse_metric_group_by_exp(pool, &group_by, &params[2], error_r))
+                               return FALSE;
                } else if (strcmp(params[1], "linear") == 0) {
                        /* <field>:linear:<min val>:<max val>:<step> */
                        if (!parse_metric_group_by_lin(pool, &group_by, &params[2], error_r))
index 183e81155a76952f986d0e50f56384c162029253..380c49b2293fb444b3053e14b4e0417329df3d9a 100644 (file)
@@ -268,6 +268,70 @@ static const struct quantized_test quantized_tests[] = {
                  { { 1000, INTMAX_MAX }, 2 },
                }
        },
+       {
+               /* start at 0 */
+               "exponential:0:6:10",
+               12,
+               { 0, 5, 10, 11, 100, 101, 500, 1000, 1001, 1000000, 1000001, 2000000 },
+               7,
+               8,
+               { { { INTMAX_MIN, 1 }, 1 },
+                 { { 1, 10 }, 2 },
+                 { { 10, 100 }, 2 },
+                 { { 100, 1000 }, 3 },
+                 { { 1000, 10000 }, 1 },
+                 { { 10000, 100000 }, 0 },
+                 { { 100000, 1000000 }, 1 },
+                 { { 1000000, INTMAX_MAX }, 2 },
+               }
+       },
+       {
+               /* start at 0 */
+               "exponential:0:6:2",
+               9,
+               { 0, 1, 2, 4, 5, 20, 64, 65, 100 },
+               7,
+               8,
+               { { { INTMAX_MIN, 1 }, 2 },
+                 { { 1, 2 }, 1 },
+                 { { 2, 4 }, 1 },
+                 { { 4, 8 }, 1 },
+                 { { 8, 16 }, 0 },
+                 { { 16, 32 }, 1 },
+                 { { 32, 64 }, 1 },
+                 { { 64, INTMAX_MAX }, 2 },
+               }
+       },
+       {
+               /* start at >0 */
+               "exponential:2:6:10",
+               12,
+               { 0, 5, 10, 11, 100, 101, 500, 1000, 1001, 1000000, 1000001, 2000000 },
+               5,
+               6,
+               { { { INTMAX_MIN, 100 }, 5 },
+                 { { 100, 1000 }, 3 },
+                 { { 1000, 10000 }, 1 },
+                 { { 10000, 100000 }, 0 },
+                 { { 100000, 1000000 }, 1 },
+                 { { 1000000, INTMAX_MAX }, 2 },
+               }
+       },
+       {
+               /* start at >0 */
+               "exponential:2:6:2",
+               9,
+               { 0, 1, 2, 4, 5, 20, 64, 65, 100 },
+               5,
+               6,
+               { { { INTMAX_MIN, 4 }, 4 },
+                 { { 4, 8 }, 1 },
+                 { { 8, 16 }, 0 },
+                 { { 16, 32 }, 1 },
+                 { { 32, 64 }, 1 },
+                 { { 64, INTMAX_MAX }, 2 },
+               }
+       },
 };
 
 static void test_stats_metrics_group_by_quantized_real(const struct quantized_test *test)