Schedule CI mirror sync workers after organization transfer
What does this MR do and why?
After a group is transferred to another organization, Organizations::Transfer::GroupsService now schedules Namespaces::ProcessSyncEventsWorker and Projects::ProcessSyncEventsWorker. Organizations::Transfer::TopLevelGroupService schedules the namespace worker, since it only moves group rows. This happens after the transfer transaction commits.
The transfer updates the organization_id of namespaces and projects with update_all, which skips the model callbacks. These callbacks are what normally schedule the two workers. Without this fix, the workers were not scheduled, so ci_namespace_mirrors and ci_project_mirrors kept the old organization id until an unrelated namespace or project save happened to trigger a worker. On a quiet self-managed instance, this could stay wrong indefinitely, since these workers do not run on a cron schedule either.
This matters because the mirror organization_id columns will be used for CI database data isolation (!254210), so the mirrors need to be updated soon after a transfer.
References
- Follow-up issue: #629374 (closed) (this MR addresses the first discussion; the documentation update is a separate MR)
- Review discussion: !254205 (comment 3841429657)
- Related: !254210
How to set up and validate locally
-
Create a second organization and a top-level group with a project in the first organization. In a Rails console:
user = User.first old_org = Organizations::Organization.first new_org = Organizations::Organization.create!(name: 'Target org', path: 'target-org') new_org.add_owner(user) group = Groups::CreateService.new(user, name: 'Sync test', path: 'sync-test', organization_id: old_org.id).execute[:group] project = Projects::CreateService.new(user, name: 'sync-test-project', namespace_id: group.id).execute -
Check the mirror rows before the transfer. Both return the old organization id:
Ci::NamespaceMirror.find_by(namespace_id: group.id).organization_id Ci::ProjectMirror.find_by(project_id: project.id).organization_id -
Run the transfer, with Sidekiq running in GDK:
Organizations::Transfer::GroupsService.new(group: group, new_organization: new_org, current_user: user).execute -
Wait a few seconds and check the mirror rows again. Both now return the new organization id. Before this change, they kept the old organization id.
-
Check that no sync events are left unprocessed. Both return 0:
Namespaces::SyncEvent.count Projects::SyncEvent.count
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.