]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-master: Fix sending events recursively
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 9 Sep 2021 10:46:24 +0000 (13:46 +0300)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Wed, 29 Sep 2021 10:09:58 +0000 (10:09 +0000)
The event sending itself may recursively trigger more events (e.g.
data_stack_grow). The previous BEGINs must have been flushed by that
time or the recursive events might be pointing to event IDs that haven't
even been sent to the stats process yet.

Fixes:
stats: Error: Client sent invalid input for UPDATE: Unknown event ID

src/lib-master/stats-client.c

index 59d9b2447255f0cafe50c734ecb9c2aaa2286b84..e456d8da2dec08034919715f3b9004b380defe77 100644 (file)
@@ -141,20 +141,23 @@ static const struct connection_vfuncs stats_client_vfuncs = {
 };
 
 static void
-stats_event_write(struct event *event, struct event *global_event,
+stats_event_write(struct stats_client *client,
+                 struct event *event, struct event *global_event,
                  const struct failure_context *ctx, string_t *str, bool begin)
 {
        struct event *merged_event;
        struct event *parent_event;
-       bool update = FALSE;
+       bool update = FALSE, flush_output = FALSE;
 
        merged_event = begin ? event_ref(event) : event_minimize(event);
        parent_event = merged_event->parent;
 
        if (parent_event != NULL) {
                if (parent_event->sent_to_stats_id !=
-                   parent_event->change_id)
-                       stats_event_write(parent_event, NULL, ctx, str, TRUE);
+                   parent_event->change_id) {
+                       stats_event_write(client, parent_event, NULL,
+                                         ctx, str, TRUE);
+               }
                i_assert(parent_event->sent_to_stats_id != 0);
        }
        if (begin) {
@@ -163,6 +166,10 @@ stats_event_write(struct event *event, struct event *global_event,
                const char *cmd = !update ? "BEGIN" : "UPDATE";
                str_printfa(str, "%s\t%"PRIu64"\t", cmd, event->id);
                event->sent_to_stats_id = event->change_id;
+               /* Flush the BEGINs early on, because the stats event writing
+                  may trigger more events recursively (e.g. data_stack_grow),
+                  which may use the BEGIN events as parents. */
+               flush_output = !update;
        } else {
                str_printfa(str, "EVENT\t%"PRIu64"\t",
                            global_event == NULL ? 0 : global_event->id);
@@ -174,12 +181,18 @@ stats_event_write(struct event *event, struct event *global_event,
        event_export(merged_event, str);
        str_append_c(str, '\n');
        event_unref(&merged_event);
+       if (flush_output) {
+               o_stream_nsend(client->conn.output, str_data(str), str_len(str));
+               str_truncate(str, 0);
+       }
 }
 
 static void
 stats_client_send_event(struct stats_client *client, struct event *event,
                        const struct failure_context *ctx)
 {
+       static int recursion = 0;
+
        if (!client->handshaked)
                return;
 
@@ -190,12 +203,18 @@ stats_client_send_event(struct stats_client *client, struct event *event,
        /* Need to send the event for stats and/or export */
        string_t *str = t_str_new(256);
 
+       if (++recursion == 0)
+               o_stream_cork(client->conn.output);
        struct event *global_event = event_get_global();
        if (global_event != NULL)
-               stats_event_write(global_event, NULL, ctx, str, TRUE);
+               stats_event_write(client, global_event, NULL, ctx, str, TRUE);
 
-       stats_event_write(event, global_event, ctx, str, FALSE);
+       stats_event_write(client, event, global_event, ctx, str, FALSE);
        o_stream_nsend(client->conn.output, str_data(str), str_len(str));
+
+       i_assert(recursion > 0);
+       if (--recursion == 0)
+               o_stream_uncork(client->conn.output);
 }
 
 static void