Fix idle-in-transaction during mark_as_unmergeable by deferring notify_conflict?
Summary
Move notify_conflict? and its downstream actions (NotificationService, TodoService) into a run_after_commit block so that Gitaly RPCs execute after the database transaction commits, eliminating idle-in-transaction time during the mark_as_unmergeable state transition.
Behind feature flag defer_notify_conflict_to_after_commit.
Problem
In the merge_status state machine, the after_transition [:unchecked, :checking] => :cannot_be_merged callback calls notify_conflict? inside the save transaction. notify_conflict? makes three Gitaly RPCs:
-
source_branch_exists?→ GitalyFindBranchRPC (viabranch_missing?) -
target_branch_exists?→ GitalyFindBranchRPC (viabranch_missing?) -
repository.can_be_merged?→ GitalyConflictsServiceRPC
These RPCs hold the DB connection idle while waiting on Gitaly responses, contributing to idle-in-transaction issues.
Additionally, TodoService.new.merge_request_became_unmergeable was also running inside the transaction unnecessarily.
Fix
Before (Gitaly RPCs inside transaction):
after_transition [:unchecked, :checking] => :cannot_be_merged do |merge_request, transition|
if merge_request.notify_conflict? # ← 3 Gitaly RPCs inside transaction
merge_request.run_after_commit do
NotificationService.new.merge_request_unmergeable(merge_request)
end
TodoService.new.merge_request_became_unmergeable(merge_request) # ← inside transaction
end
end
After (everything deferred to after commit):
after_transition [:unchecked, :checking] => :cannot_be_merged do |merge_request, transition|
merge_request.run_after_commit do
next unless merge_request.notify_conflict? # ← Gitaly RPCs after commit
NotificationService.new.merge_request_unmergeable(merge_request)
TodoService.new.merge_request_became_unmergeable(merge_request) # ← after commit
end
end
Feature flag
defer_notify_conflict_to_after_commit — rollout issue
Verification
Locally verified using ApplicationRecord.inside_transaction? that:
-
Before fix:
notify_conflict?returnsinside_transaction?: true -
After fix:
notify_conflict?,branch_missing?, andcan_be_merged?all returninside_transaction?: false
This affects the MergeRequestMergeabilityCheckWorker → MergeabilityCheckService → update_merge_status → mark_as_unmergeable call path.