[go: up one dir, main page]

Seat assignment create worker

What does this MR do and why?

This Merge Request introduces a worker responsible for creating seat assignments during the transfer of a namespace, which can be either a group or a project.

Context This MR is part of a larger effort to correctly handle seat assignment creation and deletion during group or project transfers. Specifically, it is 1 of 4 MRs addressing:

  1. Creation of seat assignments worker <- Here
  2. Deletion of outdated seat assignments worker
  3. Add create seats worker to group and project after update hooks (needs 1)
  4. Add remove outdated seats worker to group and project after update hooks (needs 2).

References

It's a part of:

How to set up and validate locally

Test 01:

  • Select a namespace with missing seat assignments (N)
  • Verify the number of seats in N with GitlabSubscriptions::SeatAssignment.by_namespace(N)
  • In the rails console, type GitlabSubscriptions::SeatAssignments::MemberTransfers::CreateSeatsWorker.perfom_async(N)
  • Verify the number of seats again with GitlabSubscriptions::SeatAssignment.by_namespace(N)

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.

SQL Plans:

create_new_seat_assignments

INSERT INTO "subscription_seat_assignments" ("namespace_id","user_id","created_at","updated_at","organization_id") VALUES (347, 141, '2025-05-13 23:20:16.800991', '2025-05-13 23:20:16.801034', 1) ON CONFLICT ("namespace_id","user_id") WHERE (namespace_id IS NOT NULL) DO NOTHING RETURNING "id"
SELECT "members"."id", "members"."access_level", "members"."source_id", "members"."source_type", "members"."user_id", "members"."notification_level", "members"."type", "members"."created_at", "members"."updated_at", "members"."created_by_id", "members"."invite_email", "members"."invite_token", "members"."invite_accepted_at", "members"."requested_at", "members"."expires_at", "members"."ldap", "members"."override", "members"."state", "members"."invite_email_success", "members"."member_namespace_id", "members"."member_role_id", "members"."expiry_notified_at", "members"."request_accepted_at" FROM ((SELECT "members"."id", "members"."access_level", "members"."source_id", "members"."source_type", "members"."user_id", "members"."notification_level", "members"."type", "members"."created_at", "members"."updated_at", "members"."created_by_id", "members"."invite_email", "members"."invite_token", "members"."invite_accepted_at", "members"."requested_at", "members"."expires_at", "members"."ldap", "members"."override", "members"."state", "members"."invite_email_success", "members"."member_namespace_id", "members"."member_role_id", "members"."expiry_notified_at", "members"."request_accepted_at" FROM "members" WHERE "members"."source_id" IN (SELECT "namespaces"."id" FROM UNNEST(
  COALESCE(
    (SELECT ids FROM (SELECT "namespace_descendants"."self_and_descendant_group_ids" AS ids FROM "namespace_descendants" WHERE "namespace_descendants"."outdated_at" IS NULL AND "namespace_descendants"."namespace_id" = 9970) cached_query),
    (SELECT ids FROM (SELECT ARRAY_AGG("namespaces"."id") AS ids FROM (SELECT namespaces.traversal_ids[array_length(namespaces.traversal_ids, 1)] AS id FROM "namespaces" WHERE "namespaces"."type" = 'Group' AND (traversal_ids @> ('{9970}'))) namespaces) consistent_query))
) AS namespaces(id)
) AND "members"."source_type" = 'Namespace')
UNION ALL
(SELECT "members"."id", "members"."access_level", "members"."source_id", "members"."source_type", "members"."user_id", "members"."notification_level", "members"."type", "members"."created_at", "members"."updated_at", "members"."created_by_id", "members"."invite_email", "members"."invite_token", "members"."invite_accepted_at", "members"."requested_at", "members"."expires_at", "members"."ldap", "members"."override", "members"."state", "members"."invite_email_success", "members"."member_namespace_id", "members"."member_role_id", "members"."expiry_notified_at", "members"."request_accepted_at" FROM "members" WHERE "members"."source_id" IN (SELECT "projects"."id" FROM UNNEST(
  COALESCE(
    (SELECT ids FROM (SELECT "namespace_descendants"."all_project_ids" AS ids FROM "namespace_descendants" WHERE "namespace_descendants"."outdated_at" IS NULL AND "namespace_descendants"."namespace_id" = 9970) cached_query),
    (SELECT ids FROM (SELECT ARRAY_AGG("projects"."id") AS ids FROM (SELECT "projects"."id" FROM "projects" WHERE "projects"."namespace_id" IN (SELECT "namespaces"."id" FROM UNNEST(
  COALESCE(
    (SELECT ids FROM (SELECT "namespace_descendants"."self_and_descendant_group_ids" AS ids FROM "namespace_descendants" WHERE "namespace_descendants"."outdated_at" IS NULL AND "namespace_descendants"."namespace_id" = 9970) cached_query),
    (SELECT ids FROM (SELECT ARRAY_AGG("namespaces"."id") AS ids FROM (SELECT namespaces.traversal_ids[array_length(namespaces.traversal_ids, 1)] AS id FROM "namespaces" WHERE "namespaces"."type" = 'Group' AND (traversal_ids @> ('{9970}'))) namespaces) consistent_query))
) AS namespaces(id)
)) projects) consistent_query))
) AS projects(id)
) AND "members"."source_type" = 'Project')) members
SELECT 
    "members"."id",
    "members"."access_level",
    "members"."source_id",
    "members"."source_type",
    "members"."user_id",
    "members"."notification_level",
    "members"."type",
    "members"."created_at",
    "members"."updated_at",
    "members"."created_by_id",
    "members"."invite_email",
    "members"."invite_token",
    "members"."invite_accepted_at",
    "members"."requested_at",
    "members"."expires_at",
    "members"."ldap",
    "members"."override",
    "members"."state",
    "members"."invite_email_success",
    "members"."member_namespace_id",
    "members"."member_role_id",
    "members"."expiry_notified_at",
    "members"."request_accepted_at"
FROM "members"
WHERE "members"."type" = 'ProjectMember'
    AND "members"."source_id" = 45
    AND "members"."source_type" = 'Project'
    AND "members"."requested_at" IS NULL;
Edited by Jorge Cook

Merge request reports

Loading