Skip to content

Query optimization - #670

Merged
stephenfin merged 4 commits into
getpatchwork:mainfrom
fstachura:query-optimization
Oct 15, 2026
Merged

stephenfin merged 4 commits into
getpatchwork:mainfrom
fstachura:query-optimization

Conversation

@fstachura

@fstachura fstachura commented Aug 13, 2026 •

Copy link
Copy Markdown
Contributor

I noticed that queries made by Patchwork can be pretty slow on larger databases.
This PR changes two larger things that seemed to cause performance issues.

I split the query in generic_list into a filtering part and an aggregation part. In the first part, only patch ids are queried. Then, another query is made to retrieve more detailed patch data. I'm not 100% sure, but it seems that MySQL and Postgres may compute all the fields (including JOINs) even for rows that don't make it into the final result due to LIMIT/OFFSET which negatively impacts performance.

I added a new index. The first three fields of the index are fields that the default patch view uses to filter patches - project_id, archived and state. The fourth field is the field used for the default order - date, sorted descending (unlike the current covering index). I'm 100% sure that project_id should be the first field in the index, and that date should be after all the fields that are most commonly used in filters.

In my tests, I have noticed substantial performance improvements on large databases (>1 million patches), on a low-end machine (~2018-laptop tier, 16GB RAM, 2 cores, 2 threads, SSD) on both Postgres and MySQL. I can share more details about my tests, but to keep it short: on main, some queries would take 5+ seconds (average on MySQL was 2 seconds, on Postgres some would cause a timeout), with these patches the average is <300 ms. (I tested with default date descending ordering, on 5 scenarios: default filters, filtering by submitter, delegate, submitter and delegate, state. I did not do stress-tests, just "one-shot"). Query plans also became shorter, in some cases were replaced by straightforward index lookup.

https://dev.mysql.com/doc/refman/8.4/en/order-by-optimization.html

@fstachura
fstachura force-pushed the query-optimization branch 3 times, most recently from 1a7374b to 55bdbad Compare August 13, 2026 12:39
This property is not used anymore, the last reference was removed
in 17726d7 (views: Move and refactor patch-forms).
Computing it may execute an unnecessary count query.
Patch list is pretty slow partially because it filters on a query
with a lot of extra fields. For example, I suspect that the tag count
fields are computed even for rows that did not make it into the final
result, same with joins.
This patch changes this by separating the filtering stage, in which only
patch IDs are queried, and the aggregation stage.
fstachura and others added 2 commits October 15, 2026 22:28
The new index uses project_id as the first key. This is important, as it
seems that almost all web queries filter over project_id. The index also
includes the submission date sorted descending, as this is the default
sorting key for the patches list.

Co-authored-by: Stephen Finucane <stephen@that.guru>
Allowing the users to set an arbitrary number of items per page may
cause excessive load on the database. For example, a request to the
patch list has to compute the number of tags for each listed patch.
@stephenfin
stephenfin enabled auto-merge October 15, 2026 21:34
@stephenfin
stephenfin added this pull request to the merge queue Oct 15, 2026
Merged via the queue into getpatchwork:main with commit fb92a0e Oct 15, 2026
11 checks passed
@stephenfin

Copy link
Copy Markdown
Member

I added a missing migration and placed -date ahead of state since the default view is to order by date and doing this avoids additional sorts if you have more than one state selected. I hope this works for you. Please let me know if you disagree

@fstachura

fstachura commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor Author

Sorry about the migration.

I ran my tests again and the results are inconclusive, Postgres did slightly better and Mysql slightly worse. Differences were less than 200ms and the results are still much better than baseline, so this is not meant as an argument for one or the other option, just LGTM and FYI. I would leave it as it is merged now.

Thanks!

@stephenfin

Copy link
Copy Markdown
Member

Thanks for the additional validation 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants