Refactor IssuableFinder into separate methods that can be re-used on their own
IssuableFinder is a very complex class used for finding issues based on a large number of criteria. Because of the way it works the queries it produces perform really badly, even for simple cases. This particular finder has been a problem in the past and keeps popping up (e.g. in https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1719). I think it's now time we start seriously refactoring it.
To do so I envision a setup where for every type of issues collection (public issues of public projects, issues of projects you have access to, etc) are retrieved using a separate query that is as optimized as possible. Such a query should take care of confidential issues, project features, etc where necessary (and only if necessary) directly in the SQL query. For project features this is as simple as joining the table, for a lot of other things we can use project_authorizations
(now that it's guaranteed to have data for all users).
Some collections that I can think:
- public issues of public or internal projects (if you're logged in as an internal user)
- public issues of public projects (if you're not logged in)
- issues of projects for which you have reporter access or greater
- This can be done by just querying
project_authorizations
- confidential issues of projects for which you have guest access and you're the assignee or author of the issue
- This is the only one that needs to filter by
confidential
In all cases we also need to filter out issues based on project_features
, but this can be done by joining the table and just adding an additional WHERE. In all cases we can join project_features
onto issues
using issues.project_id
, removing the need for first filtering projects
(though for the first 2 queries we need to join projects
anyway).
We then change IssuableFinder to basically work as follows:
- Build the ActiveRecord relations for the above queries, store them somewhere
- Add WHEREs to filter by labels, etc
- UNION the relations together using
Issue.unscoped.from(union-here).order(if-necessary)
If done correctly this should produce code/queries that are easy to maintain while still performing as optimal as possible.