Skip authorized projects refresh for new group owner
What does this MR do and why?
When a group is created (POST /api/:version/groups, the UI, onboarding/registration, imports, etc.), the creator is added as the group's owner. That membership triggers a high-urgency AuthorizedProjectsWorker run via Member#refresh_member_authorized_projects → UserProjectAccessChangedService (default HIGH_PRIORITY).
AuthorizedProjectsWorker does a full recompute of the user's authorized-project tree (via Gitlab::ProjectAuthorizations#calculate), not an event-scoped delta. A brand-new group has no projects, so this refresh can never add or remove a project_authorizations row — it's guaranteed wasted work. On large namespaces this recompute regularly exceeds the worker's 10s target duration, so group creation has been a recurring source of AuthorizedProjectsWorker target-duration breaches.
This MR skips that refresh for the new group's owner. Every other member-creation path (adding members to existing groups, transfers, SCIM/LDAP/SAML sync) is unchanged and keeps the immediate high-urgency refresh.
Impact
Over the past ~4 weeks in production, AuthorizedProjectsWorker exceeded its 10s target duration 66k+ times, of which 13k+ (~20%) were triggered by group creation. This change eliminates that ~20% of overruns.
All `AuthorizedProjectsWorker` runs exceeding the 10s target (66,368):
json.class:"AuthorizedProjectsWorker" AND json.duration_s>10 AND json.target_duration_s:10Of those, the ones triggered by group creation (13,525):
json.class:"AuthorizedProjectsWorker" AND json.duration_s>10 AND json.target_duration_s:10
AND (
json.meta.root_caller_id:"GroupsController#create"
OR json.meta.root_caller_id:"GitlabGroupsController#create"
OR json.meta.root_caller_id.keyword:"POST /api/:version/groups"
OR json.meta.root_caller_id.keyword:"POST /api/:version/groups/import"
OR json.meta.root_caller_id:"GroupImportWorker"
OR json.meta.root_caller_id:"BulkImports::PipelineWorker"
)How
A transient skip_authorized_projects_refresh flag is threaded from Groups::CreateService down to the owner Member record — mirroring the existing importing flag pattern:
Groups::CreateService#create_group → Group#add_owner(**args) → Members::CreatorService (parsed_args + member_attributes) → owner Member → GroupMember#refresh_member_authorized_projects returns early when the flag is set.
The flag check lives at the single Groups::CreateService call site (the only place that knows the group is brand new), so all group-creation entry points are covered uniformly while the transient attribute stays a pure mechanism.
Why is this safe?
The "new group has no projects at add_owner time" invariant is guaranteed by Groups::CreateService itself (@group.save then @group.add_owner; no projects are created in the transaction), so it holds for all call sites — including imports and personal-project transfers, where projects/members are restored in later steps that trigger their own refreshes.
Confirmed empirically in production (same source, past ~4 weeks): group-creation AuthorizedProjectsWorker refreshes have never added a project_authorizations rows.
Group-creation refreshes that added ≥1 row → 0:
json.event:"authorized_projects_refresh"
AND (
json.meta.root_caller_id:"GroupsController#create"
OR json.meta.root_caller_id:"GitlabGroupsController#create"
OR json.meta.root_caller_id.keyword:"POST /api/:version/groups"
OR json.meta.root_caller_id.keyword:"POST /api/:version/groups/import"
OR json.meta.root_caller_id:"GroupImportWorker"
OR json.meta.root_caller_id:"BulkImports::PipelineWorker"
)
AND json.authorized_projects_refresh.rows_added_count>0The same population with `rows_added_count: 0` → 274,271:
Query used (grouped by the entry points that trigger group creation):
json.event:"authorized_projects_refresh"
AND (
json.meta.root_caller_id:"GroupsController#create"
OR json.meta.root_caller_id:"GitlabGroupsController#create"
OR json.meta.root_caller_id.keyword:"POST /api/:version/groups"
OR json.meta.root_caller_id.keyword:"POST /api/:version/groups/import"
OR json.meta.root_caller_id:"GroupImportWorker"
OR json.meta.root_caller_id:"BulkImports::PipelineWorker"
)
AND json.authorized_projects_refresh.rows_added_count<1Verified at runtime
GroupMember#refresh_member_authorized_projects confirms, before skipping, that the member record is newly created and the owning group has no projects and no inbound group/project shares that could grant access.
Feature flag
Behind skip_authorized_projects_refresh_for_new_group, checked with current_user as the actor.
- Enabled → owner refresh on group creation is skipped.
- Disabled → current behavior (high-urgency
AuthorizedProjectsWorker) is preserved unchanged.
Rollout issue:



