Preload Gitaly data in ensure_merge_request_diff

Summary

Pass preload_gitaly: true in ensure_merge_request_diff so that Gitaly RPC calls (fetching commits and diffs) happen before the database transaction, reducing idle-in-transaction time during new merge request creation.

This applies the same optimization already used in ReloadDiffsService and ReloadMergeHeadDiffService to the initial diff creation path in NewMergeRequestWorkerAfterCreateService.

Details

In the production MR creation flow:

  1. MergeRequests::CreateService sets skip_ensure_merge_request_diff = true, so the after_create callback on MergeRequest is skipped during save!.
  2. After the transaction commits, NewMergeRequestWorker enqueues and calls AfterCreateService#execute.
  3. AfterCreateService calls merge_request.ensure_merge_request_diff directly — outside any DB transaction.
  4. ensure_merge_request_diff calls create_merge_request_diff(preload_gitaly: true).

With the preload_gitaly flag:

  • A new MergeRequestDiff is built (not yet persisted).
  • preload_gitaly_data is called, which fetches commits and diffs from Gitaly via reversed_compare_commits_preloaded and compare_diffs_preloaded — both strong_memoized. This happens outside any transaction.
  • save! is then called, which opens a transaction and triggers the after_create :save_git_content callback. Inside save_git_content, save_commits and save_diffs call the same memoized methods, so they use cached data — no Gitaly RPCs occur inside the transaction.

Without the flag (current behavior), merge_request_diffs.create! opens a transaction first, then the after_create :save_git_content callback makes Gitaly RPCs inside the transaction, causing idle-in-transaction while waiting on Gitaly.

Test

Added specs to verify ensure_merge_request_diff passes preload_gitaly: true (or false) to create_merge_request_diff depending on the feature flag state.

Edited by Marc Shaw

Merge request reports

Loading