Eagerly load DiffCollection in preload_gitaly_data
Summary
preload_gitaly_data creates a DiffCollection wrapping a lazy gRPC stream (DiffStitcher) but does not consume it. The actual Gitaly CommitDiff streaming RPC data transfer happens when DiffCollection#each is first called inside save_diffs → real_size → populate!, which runs inside the after_create :save_git_content callback — meaning it executes inside the implicit save! database transaction.
For large MRs with 1000+ changed files, this holds the DB transaction open for seconds while streaming diff data from Gitaly.
What this MR does
Adds eager_load_diff_collection which force-populates the DiffCollection (consuming the gRPC stream) during preload_gitaly_data, before save! is called. This moves the Gitaly network I/O outside the database transaction.
Guarded by the eager_load_diff_collection feature flag.
Proof
Instrumented testing confirms:
@populated after preload |
gRPC stream consumed inside txn | |
|---|---|---|
| Without FF |
nil (lazy) |
true |
| With FF |
true (eager) |
nil (already done) |
Both produce identical results (same files, commits, state).
Call chain affected
MergeRequestMergeabilityCheckWorker
→ MergeabilityCheckService#execute
→ ReloadMergeHeadDiffService#recreate_merge_head_diff
→ merge_head_diff.preload_gitaly_data ← fix is here
→ merge_head_diff.save! ← transaction opens here
Feature Flag
eager_load_diff_collection — scoped to project, default disabled.