diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index 42a15234e57..ac18c17dc61 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -79,7 +79,7 @@ class ProjectsFinder < UnionFinder elsif min_access_level? current_user.authorized_projects(params[:min_access_level]) else - if private_only? + if private_only? || impossible_visibility_level? current_user.authorized_projects else Project.public_or_visible_to_user(current_user) @@ -96,6 +96,30 @@ class ProjectsFinder < UnionFinder end end + # This is an optimization - surprisingly PostgreSQL does not optimize + # for this. + # + # If the default visiblity level and desired visiblity level filter cancels + # each other out, don't use the SQL clause for visibility level in + # `Project.public_or_visible_to_user`. In fact, this then becames equivalent + # to just authorized projects for the user. + # + # E.g. + # (EXISTS() OR projects.visibility_level IN (10,20)) + # AND "projects"."visibility_level" = 0 + # + # is essentially + # EXISTS() AND "projects"."visibility_level" = 0 + # + # See https://gitlab.com/gitlab-org/gitlab/issues/37007 + def impossible_visibility_level? + return unless params[:visibility_level].present? + + public_visibility_levels = Gitlab::VisibilityLevel.levels_for_user(current_user) + + !public_visibility_levels.include?(params[:visibility_level]) + end + def owned_projects? params[:owned].present? end diff --git a/changelogs/unreleased/projects_finder_visibility_optimization.yml b/changelogs/unreleased/projects_finder_visibility_optimization.yml new file mode 100644 index 00000000000..9d6d626d5cb --- /dev/null +++ b/changelogs/unreleased/projects_finder_visibility_optimization.yml @@ -0,0 +1,5 @@ +--- +title: Optimize query when Projects API requests private visibility level +merge_request: 20594 +author: +type: performance