Skip fork MR lookup for non-forks in Ci::Pipeline#all_merge_requests
What does this MR do and why?
For a branch pipeline, Ci::Pipeline#all_merge_requests scopes its commit lookup to a set of target project ids, collected by plucking target_project_id from every fork MR on (source_project_id, source_branch):
target_project_ids = merge_requests_for_source_project.from_fork.pluck(:target_project_id)
target_project_ids << project_idfrom_fork is where('source_project_id <> target_project_id'), with no LIMIT and no state filter, so the pluck walks the entire (project, branch) MR history. On projects that accumulate a very large history for one branch, the scan can exceed the statement timeout and 500 the pipeline page, which is what appears to be happening in request-for-help#4998 (details in the investigation comment). A non-fork project pays that full scan cost to typically collect nothing.
This MR adds a guard behind the ci_skip_fork_mr_lookup_for_non_forks feature flag (gitlab_com_derisk): when the project is not a fork, skip the pluck and use [project_id] directly. When it is a fork, the pluck still runs, now with .distinct (the consumer only needs the distinct set of ids). The guard treats "not a fork" as a proxy for "no cross-project MRs sourced from this project", which holds for the standard fork workflow. With the flag off, behavior is byte-for-byte unchanged.
Related: https://gitlab.com/gitlab-com/request-for-help/-/work_items/4998+.
Query plans
The changed query is the fork-target pluck. Plans captured on the GitLab.com production main replica.
Before (current master), on a large non-fork project — cold cache
SELECT target_project_id FROM merge_requests
WHERE source_project_id = 17679285 AND source_branch = 'master'
AND (source_project_id <> target_project_id);Explain Output
https://postgres.ai/console/gitlab/gitlab-production-main/sessions/53566/commands/155743
After — non-fork project (the common case)
No query is issued. project.forked? is false, so the pluck is skipped and target_project_ids = [project_id].
After — genuine fork, with .distinct — cold cache
SELECT DISTINCT target_project_id FROM merge_requests
WHERE source_project_id = 16983597 AND source_branch = 'master'
AND (source_project_id <> target_project_id);Explain Output
https://postgres.ai/console/gitlab/gitlab-production-main/sessions/53566/commands/155747
Feature flag
ci_skip_fork_mr_lookup_for_non_forks (gitlab_com_derisk, default_enabled: false), actor: project.
How to verify
This is a performance change, not a behavior change: #all_merge_requests returns the exact same MRs.
Production validation: after enabling for the affected group, confirm the merge_requests fork-pluck query disappears from the slow-query logs and the group's pipeline pages stop timing out.
Database review
- This modifies the query built by
Ci::Pipeline#all_merge_requests(via thefrom_forkscope). Please review the scope usage. - Old and new raw SQL and query plans are above.