As discussed with @albertoramos during the call, I'm marking this as a priority1severity1 for now since we did notice a degradation in the service when this was happening.
Because this search string is less than min_chars_for_partial_matching (3), it does a query like:
SELECT "projects".* FROM "projects" WHERE (EXISTS (SELECT 1 FROM "project_authorizations" WHERE "project_authorizations"."user_id" = 2880930 AND (project_authorizations.project_id = projects.id)) OR projects.visibility_level IN (10,20)) AND (("projects"."path" ILIKE '' OR "projects"."name" ILIKE '') OR "projects"."description" ILIKE '') AND "projects"."archived" = FALSE ORDER BY "projects"."last_activity_at" DESC LIMIT 21 OFFSET 0
(There's a non-printable character there)
For some reason, it's fast on #database-lab: https://explain.depesz.com/s/oJtr Perhaps Slack is filtering the character and it's just searching by empty string.
The search for a does not timeout on GitLab.com but other single characters timeout. Like symbols $, !, etc.. Query plan for searching for $: https://explain.depesz.com/s/icMP
I think the appropriate immediate path forward to resolve this problem is to limit this search to 3 or more characters, but I'm not entirely sure why this is fast on #database-lab, so I just want to make sure that putting a minimum on the term length will resolve the slowness.
As far as I can see, those queries are so slow because they access too much data.
From the query plans that @engwan posted for a and $:
$ --> accesses 14,413,195 entries of the trigram (text) index
-> Bitmap Index Scan using index_projects_on_description_trigram (actual time=107793.930..107793.930 rows=14413195 loops=1) Index Cond: (projects.description ~~* '$'::text) Buffers: shared hit=4 read=119672 I/O Timings: read=82133.545
a --> accesses 587,098 entries of the trigram (text) index but there are also matches in the other indexes so more time is spent above during the scan of the results and applying the filter:
...-> Bitmap Heap Scan on public.projects (actual time=581.479..13424.452 rows=230 loops=1) Filter: ((NOT projects.archived) AND ((alternatives: SubPlan 1 or hashed SubPlan 2) OR (projects.visibility_level = ANY ('{10,20}'::integer[])))) Rows Removed by Filter: 1453 Buffers: shared hit=3536 read=379455 dirtied=11983 written=1169 I/O Timings: read=8275.779 write=45.462 ... ... -> Bitmap Index Scan using index_projects_on_description_trigram (actual time=295.020..295.021 rows=587098 loops=1) Index Cond: (projects.description ~~* 'a'::text) Buffers: shared hit=618 read=1126 I/O Timings: read=45.418...
I think that we should limit the minimum length of the search string sent back with this query. I am not sure what the limit should be, but the query provided does not even work for the most common English trigrams.
Edit: I understand that we switch to a different type of query above 3 characters, but FYI the query provided goes bellow 5sec execution times in #database-lab at 5 character strings and above.
The initial plan by @engwan was fast on #database-lab because, as correctly guessed, the empty string was sent:
...Filter: ((NOT projects.archived) AND (((projects.path)::text ~~* ''::text) OR ((projects.name)::text ~~* ''::text) OR (projects.description ~~* ''::text)) AND ((alternatives: SubPlan 1 or hashed SubPlan 2) OR (projects.visibility_level = ANY ('{10,20}'::integer[]))))...
I run an explain using a and I got a more suitable for #database-lab execution time of 396 seconds (plan)
Edit: I understand that we switch to a different type of query above 3 characters, but FYI the query provided goes bellow 5sec execution times in #database-lab at 5 character strings and above.
Yeah, for 3 characters and above it becomes a partial search like: ILIKE '%SEARCHSTRING%'.
So is it fine to set a minimum of 3 characters for this search?