Fix race in auto-assign code-owner reviewers on MR create
What does this MR do and why?
The auto-assign code-owner reviewers chain enqueued from EE::MergeRequests::CreateService#after_create raced against the merge head diff being built.
The first MergeRequests::SyncCodeOwnerApprovalRulesWorker pass on a fresh MR ran before the diff was finalized, so Gitlab::CodeOwners.entries_for_merge_request returned an empty array, no code-owner approval rules were persisted, but AutoAssignReviewersWorker was still enqueued unconditionally. When that worker ran, CodeOwnersStrategy#select_reviewers had no code_owner? rules to read, so AssignService returned skipped('No eligible reviewers') — ServiceResponse.success, no log line, no reviewer assigned. A later inline sync (from ReloadMergeHeadDiffService) populated the rules ~1s later but didn't re-fire the chain.
Reproduced locally in GDK and on playground project MR gitlab-com/create-stage/code-review-ai-experiment-playground!80 (closed). Sample timeline on local MR 606:
| Event | Time |
|---|---|
| MR created | 09:10:34.917 |
AutoAssignReviewersWorker start |
09:10:35.289 |
AutoAssignReviewersWorker done (silent skip) |
09:10:35.329 |
| Code-owner approval rule persisted | 09:10:36.161 |
The draft -> ready path in EE::MergeRequests::UpdateService is not affected — that path enqueues AutoAssignReviewersWorker directly long after rules are synced. Verified working in the playground project: gitlab-com/create-stage/code-review-ai-experiment-playground!81 (closed).
Fix
Replace the in-band enqueue_auto_reviewer_assignment worker arg with a one-shot Redis flag (MergeRequests::ReviewerAssignment::PendingInitialAssignment, TTL 10 min):
EE::MergeRequests::CreateService#after_createcallsPendingInitialAssignment.mark(issuable)if the project has the feature enabled. The worker is enqueued without the old param.EE::MergeRequests::ReloadMergeHeadDiffServiceconsumes the pending flag and enqueuesAutoAssignReviewersWorkeronce — afterMergeabilityCheckServicehas rebuilt the merge head diff and the EE override has re-synced code-owner approval rules against it. The consume is atomic (DEL == 1), so subsequent reload paths (push refresh, recheck) see no flag and don't re-fire.
Also: the sidekiq job hash uses string keys now to silence the recurring strict_args warning (expire_unapproved_key is a Symbol).
Related
- Rollout: #590879 (closed)
- Original feature MR that motivated this verification: !234732 (merged)
How to set up and validate locally
- In Rails console, enable the FF and confirm Ultimate license:
Feature.enable(:auto_assign_code_owner_reviewers) - Pick a project with
code_ownerslicensed feature, addCODEOWNERS(e.g.* @some-user), andproject.project_setting.update!(reviewer_assignment_strategy: :code_owners). - Open an MR via the API or UI as a different user than the code owner.
- Observe in
log/sidekiq.logthe chainSyncCodeOwner done -> AutoAssign start -> doneand the code owner assigned as reviewer.
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.