MEDIUM: pattern: make pat_ref_prune() rely on pat_ref_purge_older()
When purging all of a reference, it's much more efficient to scan the
reference patterns from the reference head and delete all derivative
patterns than to scan the expressions. The only thing is that we need
to proceed both for the current and next generations, in case there is
a huge gap between the two. With this, purging 20M IP addresses in small
batches of 100 takes roughly 3 seconds.
diff --git a/src/pattern.c b/src/pattern.c
index b0b136b..9b58d35 100644
--- a/src/pattern.c
+++ b/src/pattern.c
@@ -2140,53 +2140,16 @@
#endif
}
-/* This function prunes all entries of <ref>. This function
- * prunes the associated pattern_expr. It may return before the end of
- * the list is reached, returning 0, to yield. The caller must call it
- * again. Otherwise it returns 1 once done.
+/* This function prunes all entries of <ref> and all their associated
+ * pattern_expr. It may return before the end of the list is reached,
+ * returning 0, to yield, indicating to the caller that it must call it again.
+ * until it returns non-zero. All patterns are purged, both current ones and
+ * future or incomplete ones. This is used by "clear map" or "clear acl".
*/
int pat_ref_prune(struct pat_ref *ref)
{
- struct pat_ref_elt *elt, *safe;
- struct pattern_expr *expr;
- struct bref *bref, *back;
- int loops = 0;
-
- list_for_each_entry(expr, &ref->pat, list) {
- HA_RWLOCK_WRLOCK(PATEXP_LOCK, &expr->lock);
- expr->pat_head->prune(expr);
- HA_RWLOCK_WRUNLOCK(PATEXP_LOCK, &expr->lock);
- loops++;
- /* yield often, some lists may be huge, especially those
- * having to be freed through free_pattern_tree()
- */
- if (loops > 10)
- return 0;
- }
-
- /* we trash pat_ref_elt in a second time to ensure that data is
- free once there is no ref on it */
- list_for_each_entry_safe(elt, safe, &ref->head, list) {
- list_for_each_entry_safe(bref, back, &elt->back_refs, users) {
- /*
- * we have to unlink all watchers. We must not relink them if
- * this elt was the last one in the list.
- */
- LIST_DEL(&bref->users);
- LIST_INIT(&bref->users);
- if (elt->list.n != &ref->head)
- LIST_ADDQ(&LIST_ELEM(elt->list.n, typeof(elt), list)->back_refs, &bref->users);
- bref->ref = elt->list.n;
- }
- LIST_DEL(&elt->list);
- free(elt->pattern);
- free(elt->sample);
- free(elt);
- loops++;
- if (loops > 100000)
- return 0;
- }
- return 1;
+ return pat_ref_purge_older(ref, ref->curr_gen + 1, 100) &&
+ pat_ref_purge_older(ref, ref->next_gen + 1, 100);
}
/* This function looks up any existing reference <ref> in pattern_head <head>, and