Update seat type for seat assignment on member new event

What does this MR do and why?

This code change improves how GitLab tracks and manages user seat assignments for billing purposes.

  1. Better error handling: When the system can't calculate seat types, it now returns an empty result instead of nothing, preventing potential crashes.
  2. More accurate seat tracking: The system now calculates and stores the specific type of seat each user occupies (like "base" or "free") based on their access level, rather than just tracking whether they have a seat.
  3. Smarter updates: Instead of only creating new seat records, the system can now update existing ones when a user's access level changes, ensuring billing accuracy when permissions are modified.
  4. Performance optimization: The code now caches frequently-used values like namespace and organization IDs to avoid repeated database lookups.
  5. Enhanced testing: Tests were expanded to verify the new seat type tracking and update functionality works correctly.

This merge request should only be merged after the base merge request !201292 (merged)

Database

Query

GitlabSubscriptions::SeatAssignment.upsert_all(seat_assignments, unique_by: [:namespace_id, :user_id])

SQL

INSERT INTO "subscription_seat_assignments" ("namespace_id","organization_id","user_id","seat_type","created_at","updated_at") VALUES (1, 1, 1, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT ("namespace_id","user_id") WHERE (namespace_id IS NOT NULL) DO UPDATE SET updated_at=(CASE WHEN ("subscription_seat_assignments"."organization_id" IS NOT DISTINCT FROM excluded."organization_id" AND "subscription_seat_assignments"."seat_type" IS NOT DISTINCT FROM excluded."seat_type") THEN "subscription_seat_assignments".updated_at ELSE CURRENT_TIMESTAMP END),"organization_id"=excluded."organization_id","seat_type"=excluded."seat_type" RETURNING "id"

Query Plan

https://console.postgres.ai/gitlab/gitlab-production-main/sessions/42609/commands/130346

References

Solves https://gitlab.com/gitlab-org/gitlab/-/issues/552136

Screenshots or screen recordings

Before After

How to set up and validate locally

Start your SaaS GitLab instance locally

  1. Create a parent group with Ultimate and add two child groups.
  2. Add a user to one child group with the role Guest
  3. Check the last GitlabSubscriptions::SeatAssignment.last for the seat type free
  4. Add the user to the other child group with the role Developer
  5. Ensure the same seat assignment got updated and the seat type changed to base

Extended testing

  1. Create another parent group with Ultimate
  2. Add the same user to the new group as guest
  3. See how a new seat assignment with the seat type guest gets created, and the old one does not get updated
    • Check with GitlabSubscriptions::SeatAssignment.last(2)

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.

Edited by Lukas Wanko

Merge request reports

Loading