Loading
Delete orphaned group-level rows from scim_oauth_access_tokens
Series
group_id has been dead since group SCIM tokens moved to group_scim_auth_access_tokens in 17.9.
Prerequisites — target master, any order, all must land before step 2:
| MR | What |
|---|---|
| !261148 | GroupScimAuthAccessToken gets its own serializer |
| !261149 | Real group_id only on the group SCIM API entity |
| !261150 (merged) | No SCIM token transfer on group org transfer |
Steps — sequential, one milestone apart, since each needs the previous deployed, not just merged:
| Step | MR | Milestone | What | State |
|---|---|---|---|---|
| 1 | !256720 (merged) |
19.5 | Delete orphaned group-level rows | merged |
| 2 | !256721 | 19.6 | Remove code references, add ignore_column |
open |
| 3 | !256722 | 19.7 | Drop group_id column |
open |
| 4 | !256725 | 19.8 | Remove ignore_column rule |
open |
What does this MR do and why?
When scim_oauth_access_tokens was split in 17.9 (!170565 (merged)), group-level tokens were copied to group_scim_auth_access_tokens. Rows with group_id set are orphaned — no code reads or writes them. This migration deletes them, unblocking the group_id column drop in !256722.
Data deletion
- Records: ~4,550 on GitLab.com production (all rows in the table; GitLab.com has no instance-level tokens). Self-managed: at most one row per SCIM-enabled group.
- Recovery: Every row has a counterpart in
group_scim_auth_access_tokens(verified on production, see comment below:missing=0; 213 tokens differ only from rotations after the sync stopped, and the new table is newer in every case). Backups / PITR as fallback. - User experience impact: None. Group SCIM auth is served entirely from
group_scim_auth_access_tokens; instance-level tokens are untouched.
Query plans
DELETE FROM "scim_oauth_access_tokens" WHERE "scim_oauth_access_tokens"."group_id" IS NOT NULL AND "scim_oauth_access_tokens"."id" >= 1 AND "scim_oauth_access_tokens"."id" < 1761Plan: https://console.postgres.ai/gitlab/projects/gitlab-production-main/sessions/58419/commands/163207
References
- #630160 (tracking issue)
- #624518 (sharding key audit)
- !170565 (merged) (original table split)
How to set up and validate locally
ActiveRecord::Base.logger = nil # silence SQL logging
group = Group.first
org = Organizations::Organization.first
# Seed a group-level token (the kind this migration deletes). Let the model
# generate the token; organization_id is NOT NULL by check constraint.
ScimOauthAccessToken.create!(group: group, organization: org)
puts "before: #{ScimOauthAccessToken.where.not(group_id: nil).count} group row(s)"
# Run the migration directly (re-runnable; MigrationContext#run skips
# versions already recorded in schema_migrations)
require Rails.root.join('db/post_migrate/20260923094500_delete_group_scim_oauth_access_tokens.rb')
DeleteGroupScimOauthAccessTokens.new.migrate(:up)
puts "after: #{ScimOauthAccessToken.where.not(group_id: nil).count} group row(s)"
ScimOauthAccessToken.create!(organization: org)
puts "instance token ok: #{ScimOauthAccessToken.find_for_instance.present?}"Expected output:
before: 1 group row(s)
after: 0 group row(s)
instance token ok: trueMR acceptance checklist
Evaluate this MR against the MR acceptance checklist.
Edited by Lukas Wanko