]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: event filter - Avoid using event field wildcard matching when not needed
authorJosef 'Jeff' Sipek <jeff.sipek@open-xchange.com>
Fri, 30 Apr 2021 20:59:50 +0000 (16:59 -0400)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Mon, 10 May 2021 11:42:42 +0000 (11:42 +0000)
If the right hand side of an event field comparison is not using any
wildcards, we can compare using strcasecmp() instead of the more expensive
wildcard_match_icase().

This wildcard matching avoidance change speeds up matching quite a bit.
When the desired comparison is *not* a wildcard match (i.e., the filter is
'fieldname=abc'), microbenchmarks show at least a 11% speedup in filter
matching speed.  When the comparison includes a wildcard (i.e., the filter
is 'fieldname=abc*'), microbenchmarks show approximately 0.9% filer matching
slowdown.

Since there are so many non-wildcard matches in a typical filter, this is a
very good trade-off.

src/lib/event-filter-parser.y
src/lib/event-filter-private.h
src/lib/event-filter.c

index 8f53dfd0d2ff212a617192e80a146aa43886c69d..3d5460043f8179e1233356c3ee975e9e90aaab51 100644 (file)
@@ -102,8 +102,12 @@ static struct event_filter_node *key_value(struct event_filter_parser_state *sta
                           Either we have a string, or a number with wildcards */
                        node->field.value.intmax = INT_MIN;
                }
+
+               if (wildcard_is_literal(node->field.value.str))
+                       node->type = EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT;
                break;
        case EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT:
+       case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT:
                i_unreached();
        }
 
index 09ce1a0a8a5d5e9ba1f09dd24719ee48e9560018..47ff8df3cf242cead97e1cc33566dca51f3b3f6f 100644 (file)
@@ -24,6 +24,7 @@ enum event_filter_node_type {
        EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD, /* str */
        EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION, /* str + int */
        EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY, /* cat */
+       EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT, /* field */
        EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD, /* field */
 };
 
index cd7fb7ca8de68ae96b4e967b74dbc0d8cd76609e..ff49e1b95637378eb9666233d7611f5d8ca65dce 100644 (file)
@@ -464,6 +464,7 @@ event_filter_export_query_expr(const struct event_filter_query_internal *query,
                } else
                        str_append(dest, event_filter_category_from_log_type(node->category.log_type));
                break;
+       case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT:
        case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD:
                str_append_c(dest, '"');
                event_filter_append_escaped(dest, node->field.key);
@@ -560,7 +561,7 @@ event_has_category(struct event *event, struct event_filter_node *node,
 
 static bool
 event_match_field(struct event *event, const struct event_field *wanted_field,
-                 enum event_filter_node_op op)
+                 enum event_filter_node_op op, bool use_strcmp)
 {
        const struct event_field *field;
 
@@ -581,7 +582,10 @@ event_match_field(struct event *event, const struct event_field *wanted_field,
                        /* field was removed, but it matches field="" filter */
                        return wanted_field->value.str[0] == '\0';
                }
-               return wildcard_match_icase(field->value.str, wanted_field->value.str);
+               if (use_strcmp)
+                       return strcasecmp(field->value.str, wanted_field->value.str) == 0;
+               else
+                       return wildcard_match_icase(field->value.str, wanted_field->value.str);
        case EVENT_FIELD_VALUE_TYPE_INTMAX:
                if (wanted_field->value.intmax > INT_MIN) {
                        /* compare against an integer */
@@ -610,7 +614,10 @@ event_match_field(struct event *event, const struct event_field *wanted_field,
                        }
                        char tmp[MAX_INT_STRLEN];
                        i_snprintf(tmp, sizeof(tmp), "%jd", field->value.intmax);
-                       return wildcard_match_icase(tmp, wanted_field->value.str);
+                       if (use_strcmp)
+                               return strcasecmp(field->value.str, wanted_field->value.str) == 0;
+                       else
+                               return wildcard_match_icase(tmp, wanted_field->value.str);
                }
        case EVENT_FIELD_VALUE_TYPE_TIMEVAL:
                /* there's no point to support matching exact timestamps */
@@ -647,8 +654,12 @@ event_filter_query_match_cmp(struct event_filter_node *node,
                                 strcmp(event->source_filename, node->str) != 0);
                case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY:
                        return event_has_category(event, node, log_type);
+               case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT:
+                       return event_match_field(event, &node->field, node->op,
+                                                TRUE);
                case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD:
-                       return event_match_field(event, &node->field, node->op);
+                       return event_match_field(event, &node->field, node->op,
+                                                FALSE);
        }
 
        i_unreached();
@@ -815,6 +826,7 @@ event_filter_query_update_category(struct event_filter_query_internal *query,
        case EVENT_FILTER_NODE_TYPE_EVENT_NAME_EXACT:
        case EVENT_FILTER_NODE_TYPE_EVENT_NAME_WILDCARD:
        case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION:
+       case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT:
        case EVENT_FILTER_NODE_TYPE_EVENT_FIELD_WILDCARD:
                break;
        case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY: