Use a materialized CTE for by_commit_sha's metadata lookup

What does this MR do and why?

For target projects reading the new partitioned merge_request_diff_commits table, MergeRequestDiff.by_commit_sha joined merge_request_commits_metadata as a plain INNER JOIN. Postgres inlines that join and applies sha as a per-row Filter, doing one metadata lookup per commit in the diff instead of using the (project_id, sha) index once. Cost scales with the number of commits in the diff.

This MR lets the metadata lookup be built as a WITH ... AS MATERIALIZED CTE instead. MATERIALIZED is an optimisation fence; without it Postgres re-inlines the CTE and the plan is unchanged.

The fence is opt-in, via a materialize_metadata: keyword argument on MergeRequestDiff.by_commit_sha, defaulting to false and forwarded through MergeRequest.by_commit_sha. It only pays off when the outer query already narrows to a handful of merge_requests rows. When the outer query is unselective, the fence blocks the EXISTS from being pulled into a semi-join and the plan loses its parallel workers, a net loss.

Call sites

Call site Outer predicate Fence
Ci::Pipeline#all_merge_requests source_project_id + source_branch yes
MergeRequest.by_related_commit_sha (via MergeRequestsFinder) target_project_id no
Note#merge_requests target_project_id no
Projects::CleanupService merge_request_diffs.project_id no

Only Ci::Pipeline#all_merge_requests opts in. The other three emit SQL byte-identical to master, so their plans are unchanged.

Database

Query shape for the opted-in call site, with binds in place of literals:

Query shape, before and after

Before:

SELECT merge_request_diffs.*
FROM (
  (SELECT merge_request_diffs.*
   FROM merge_request_diffs
   INNER JOIN merge_request_diff_commits
     ON merge_request_diffs.id = merge_request_diff_commits.merge_request_diff_id
   INNER JOIN merge_request_commits_metadata
     ON merge_request_commits_metadata.id = merge_request_diff_commits.merge_request_commits_metadata_id
    AND merge_request_commits_metadata.project_id IN ($1)
   WHERE merge_request_commits_metadata.sha = $2
     AND merge_request_diff_commits.project_id = $1)
) merge_request_diffs;

After:

WITH target_metadata AS MATERIALIZED (
  SELECT merge_request_commits_metadata.id
  FROM merge_request_commits_metadata
  WHERE merge_request_commits_metadata.project_id = $1
    AND merge_request_commits_metadata.sha = $2
)
SELECT merge_request_diffs.*
FROM (
  (SELECT merge_request_diffs.*
   FROM merge_request_diffs
   INNER JOIN merge_request_diff_commits
     ON merge_request_diffs.id = merge_request_diff_commits.merge_request_diff_id
    AND merge_request_diff_commits.project_id IN ($1)
   WHERE merge_request_diff_commits.merge_request_commits_metadata_id IN (SELECT id FROM target_metadata))
) merge_request_diffs;

Measured effect on the opted-in query, using shared buffer hits on a warm cache:

Case Before After
SHA absent from metadata 111 10
SHA present, 19-commit diff 21 25
SHA present in metadata but not in a 686-commit diff 3975 22

The point is that cost becomes independent of diff size. The middle row costs 4 buffers more, which is the constant CTE setup.

Query plans

Database Lab, gitlab-production-main:

Query Before After
Ci::Pipeline#all_merge_requests, SHA absent https://postgres.ai/console/gitlab/gitlab-production-main/sessions/55974/commands/160025 https://postgres.ai/console/gitlab/gitlab-production-main/sessions/55974/commands/160027
Ci::Pipeline#all_merge_requests, SHA present, small diff https://postgres.ai/console/gitlab/gitlab-production-main/sessions/55974/commands/160029 https://postgres.ai/console/gitlab/gitlab-production-main/sessions/55974/commands/160032
Ci::Pipeline#all_merge_requests, SHA present, 686-commit diff https://postgres.ai/console/gitlab/gitlab-production-main/sessions/56045/commands/160035 https://postgres.ai/console/gitlab/gitlab-production-main/sessions/56045/commands/160037

References

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Eugenia Grieff

Merge request reports

Loading
Loading