Record Orbit beta and trial enrollments
What does this MR do and why?
Records why a top-level group can use Orbit: the beta or a trial. The trial flow reads it first.
- The
orbit_namespace_enrollmentstable andAnalytics::Orbit::NamespaceEnrollmentrecord why a group can use Orbit (sourceisdirect,beta, ortrial, plustrial_ended_at).directis for groups that turn on Orbit after GA. The row is keyed bynamespace_id, so each group has one row. Unlike theknowledge_graph_enabled_namespacesrow, this row stays when Orbit is off. - A post-deploy backfill, and
NamespaceEnrollment.record_betaon every enable (Owner, admin API, and the Self-Managed auto-index worker). The enable and the enrollment write happen in one transaction. - Auditors get
read_orbit_trial, as requested in the !258545 (merged) review.
| Event | Enrollment |
|---|---|
| Orbit is on before this MR | beta (backfill on GitLab.com) |
| Orbit is turned on | beta |
| The group has a row | No change, so a trial row stays trial |
| Orbit is turned off | No change |
| The group moves under another group | Row deleted |
| Subgroup or user namespace | No row |
The row points to the group, so it stays when Orbit is off. A later MR writes the trial row.
References
https://gitlab.com/gitlab-org/orbit/-/work_items/19
Closes https://gitlab.com/gitlab-org/orbit/knowledge-graph/-/work_items/1335
Closes https://gitlab.com/gitlab-org/orbit/knowledge-graph/-/work_items/1336
How to set up and validate locally
Run bin/rails db:migrate. Then, in the Rails console:
Analytics::Orbit::NamespaceEnrollment.group(:source).countTurn Orbit on for a top-level group as an Owner, and check the row again:
mutation { orbitUpdate(input: { groupPath: "toolbox", enabled: true }) { errors } }Demo against the local stack: the backfill, Orbit off and on (the row stays), a new beta row, a trial row that stays, and the admin API for a group and a user namespace.
Migration output
main: == 20260929214327 CreateOrbitNamespaceEnrollments: migrating ==================
main: -- create_table(:orbit_namespace_enrollments)
main: == 20260929214327 CreateOrbitNamespaceEnrollments: migrated (0.0657s) =========
main: == 20260929214328 BackfillOrbitNamespaceEnrollmentsBeta: migrated (0.0897s) ===
main: == 20260929214328 BackfillOrbitNamespaceEnrollmentsBeta: reverted (0.0194s) ===
main: == 20260929214327 CreateOrbitNamespaceEnrollments: reverted (0.0657s) =========Query plans
The backfill is safe for GitLab.com. On a production clone, a batch of 1,000 groups takes 3.5 s with a cold cache, and the full table takes 9.2 s in one statement.
| Query | Time (cold cache) | Plan |
|---|---|---|
| One batch (1,000 IDs) | 3.5 s | Plan |
| Full table, one statement | 9.2 s | Plan |
NamespaceEnrollment.record_beta runs two queries. On GitLab.com, the toggle service and the admin API pass one group. The auto-index worker runs only on Self-Managed and passes up to 1,000 groups.
SELECT "namespaces"."id" FROM "namespaces"
WHERE "namespaces"."type" = 'Group' AND "namespaces"."parent_id" IS NULL AND "namespaces"."id" IN (9970);
INSERT INTO "orbit_namespace_enrollments" ("namespace_id","source","created_at","updated_at")
VALUES (9970, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
ON CONFLICT ("namespace_id") DO NOTHING RETURNING "namespace_id";record_beta queries |
Time | Plan |
|---|---|---|
| Select top-level groups, 1 ID | 2.4 ms | Plan |
| Select top-level groups, 1,000 IDs | 9.1 ms | Plan |
| Insert 1 row | 4.0 ms | Plan |
Plan details
- Index scans only:
knowledge_graph_enabled_namespaces_pkeyfor the batch, andindex_groups_on_parent_id_idfor the join tonamespaces. - Most of the time is cold reads: 1.0 s of disk I/O on
namespaces, and 2.4 s in the foreign key trigger (one check for each row). The production cache fornamespacesis warm. - The migration runs 4 batches outside a DDL transaction, so each batch commits by itself.
- Locks:
AccessShareLockon the tables, andFOR KEY SHAREfrom the foreign key check on at most 1,000namespacesrows for each batch. These row locks block only deletes and key changes of those groups.
MR acceptance checklist
See the MR acceptance checklist.