Fix recently-introduced performance problem in ts_headline().
authorTom Lane <[email protected]>
Fri, 31 Jul 2020 15:43:13 +0000 (11:43 -0400)
committerTom Lane <[email protected]>
Fri, 31 Jul 2020 15:43:13 +0000 (11:43 -0400)
The new hlCover() algorithm that I introduced in commit c9b0c678d
turns out to potentially take O(N^2) or worse time on long documents,
if there are many occurrences of individual query words but few or no
substrings that actually satisfy the query.  (One way to hit this
behavior is with a "common_word & rare_word" type of query.)  This
seems unavoidable given the original goal of checking every substring
of the document, so we have to back off that idea.  Fortunately, it
seems unlikely that anyone would really want headlines spanning all of
a long document, so we can avoid the worse-than-linear behavior by
imposing a maximum length of substring that we'll consider.

For now, just hard-wire that maximum length as a multiple of max_words
times max_fragments.  Perhaps at some point somebody will argue for
exposing it as a ts_headline parameter, but I'm hesitant to make such
a feature addition in a back-patched bug fix.

I also noted that the hlFirstIndex() function I'd added in that
commit was unnecessarily stupid: it really only needs to check whether
a HeadlineWordEntry's item pointer is null or not.  This wouldn't make
all that much difference in typical cases with queries having just
a few terms, but a cycle shaved is a cycle earned.

In addition, add a CHECK_FOR_INTERRUPTS call in TS_execute_recurse.
This ensures that hlCover's loop is cancellable if it manages to take
a long time, and it may protect some other TS_execute callers as well.

Back-patch to 9.6 as the previous commit was.  I also chose to add the
CHECK_FOR_INTERRUPTS call to 9.5.  The old hlCover() algorithm seems
to avoid the O(N^2) behavior, at least on the test case I tried, but
nonetheless it's not very quick on a long document.

Per report from Stephen Frost.

Discussion: https://postgr.es/m/20200724160535[email protected]

src/backend/utils/adt/tsvector_op.c

index 4289bef5da7133435bacc90b342c558b4b245117..532363c492ca0a0eab2fa50b4e5b9d4ebcd8e395 100644 (file)
@@ -684,6 +684,9 @@ TS_execute(QueryItem *curitem, void *checkval, bool calcnot,
    /* since this function recurses, it could be driven to stack overflow */
    check_stack_depth();
 
+   /* ... and let's check for query cancel while we're at it */
+   CHECK_FOR_INTERRUPTS();
+
    if (curitem->type == QI_VAL)
        return chkcond(checkval, (QueryOperand *) curitem);