From: Josef 'Jeff' Sipek Date: Mon, 1 Apr 2019 10:23:41 +0000 (+0300) Subject: lib: Add event_minimize() X-Git-Tag: 2.3.9~484 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=24b1aba3b706c79b32f0fe5b8d291a58fbcb0030;p=thirdparty%2Fdovecot%2Fcore.git lib: Add event_minimize() At times, we want to simplify the structure of events before sending them to the stats process thereby minimizing the amount of data transfered and the amount of work the stats process has to do. This simplification before sending allows us to incur the CPU penalty in the (many) sending processes instead of in the (sole & single-threaded) stats process. Unlike the code in lib-master, this implementation flattens part of the parent chain backwards so that parents' fields are properly overridden by their children. It exists in lib because it shares code with the recently added event_flatten(). --- diff --git a/src/lib/lib-event.c b/src/lib/lib-event.c index a9ff06f075..c88c1f7743 100644 --- a/src/lib/lib-event.c +++ b/src/lib/lib-event.c @@ -162,6 +162,120 @@ struct event *event_flatten(struct event *src) return dst; } +static inline void replace_parent_ref(struct event *event, struct event *new) +{ + if (event->parent == new) + return; /* no-op */ + + if (new != NULL) + event_ref(new); + + event_unref(&event->parent); + + event->parent = new; +} + +/* + * Minimize the event and its ancestry. + * + * In general, the chain of parents starting from this event can be divided + * up into four consecutive ranges: + * + * 1. the event itself + * 2. a range of events that should be flattened into the event itself + * 3. a range of trivial (i.e., no categories or fields) events that should + * be skipped + * 4. the rest of the chain + * + * Except for the first range, the event itself, the remaining ranges can + * have zero events. + * + * As the names of these ranges imply, we want to flatten certain parts of + * the ancestry, skip other parts of the ancestry and leave the remainder + * untouched. + * + * For example, suppose that we have an event (A) with ancestors forming the + * following graph: + * + * A -> B -> C -> D -> E -> F + * + * Further, suppose that B, C, and F contain some categories or fields but + * have not yet been sent to an external process that knows how to reference + * previously encountered events, and D contains no fields or categories of + * its own (but it inherits some from E and F). + * + * We can define the 4 ranges: + * + * A: the event + * B-C: flattening + * D: skipping + * E-end: the rest + * + * The output would therefore be: + * + * G -> E -> F + * + * where G contains the fields and categories of A, B, and C (and trivially + * D beacuse D was empty). + * + * Note that even though F has not yet been sent out, we send it now because + * it is part of the "rest" range. + * + * TODO: We could likely apply this function recursively on the "rest" + * range, but further investigation is required to determine whether it is + * worth it. + */ +struct event *event_minimize(struct event *event) +{ + struct event *flatten_bound; + struct event *skip_bound; + struct event *new_event; + struct event *cur; + + if (event->parent == NULL) + return event_ref(event); + + /* find the bound for field/category flattening */ + flatten_bound = NULL; + for (cur = event->parent; cur != NULL; cur = cur->parent) { + if (!cur->id_sent_to_stats && + timeval_cmp(&cur->tv_created_ioloop, + &event->tv_created_ioloop) == 0) + continue; + + flatten_bound = cur; + break; + } + + /* continue to find the bound for empty event skipping */ + skip_bound = NULL; + for (; cur != NULL; cur = cur->parent) { + if (!cur->id_sent_to_stats && + (!array_is_created(&cur->fields) || array_is_empty(&cur->fields)) && + (!array_is_created(&cur->categories) || array_is_empty(&cur->categories))) + continue; + + skip_bound = cur; + break; + } + + /* fast path - no flattening and no skipping to do */ + if ((event->parent == flatten_bound) && + (event->parent == skip_bound)) + return event_ref(event); + + new_event = event_dup(event); + + /* flatten */ + event_flatten_recurse(new_event, event, flatten_bound); + replace_parent_ref(new_event, flatten_bound); + + /* skip */ + replace_parent_ref(new_event, skip_bound); + + return new_event; +} + #undef event_create struct event *event_create(struct event *parent, const char *source_filename, unsigned int source_linenum) diff --git a/src/lib/lib-event.h b/src/lib/lib-event.h index 63f7dbb099..690b0fd3a8 100644 --- a/src/lib/lib-event.h +++ b/src/lib/lib-event.h @@ -92,6 +92,13 @@ struct event *event_dup(const struct event *source); A new reference to the source event is returned if no flattening was needed. */ struct event *event_flatten(struct event *src); +/* Returns a minimized version of the source event. + Remove parents with no fields or categories, attempt to flatten fields + and categories to avoid sending one-off parent events. (There is a more + detailed description in a comment above the function implementation.) + A new reference to the source event is returned if no simplification + occured. */ +struct event *event_minimize(struct event *src); /* Copy all categories from source to dest. Only the categories in source event itself are copied. Parent events' categories aren't copied. */