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

How to set up and validate locally

  1. 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
  2. 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
  3. Run the transfer, with Sidekiq running in GDK:

    Organizations::Transfer::GroupsService.new(group: group, new_organization: new_org, current_user: user).execute
  4. 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.

  5. 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.

🤖 Generated with Claude Code

Edited by Rutger Wessels

Merge request reports

Loading
Loading