Loading
Dynamically determine classes to update when transferring from TLG to new org
What does this MR do and why?
This MR aims to simplify the transfer of a top-level group to an organization when updating records that are associated to a user.
This is achieved by finding models that have a single association to an user and organization, and then query and update the rows via the foreign key that's linked to a user.
There are some models that we cannot transfer automatically, and those will be dealt with manually in other MR's.
Notes
Check if index exists on user foreign key
#!/usr/bin/env ruby
# frozen_string_literal: true
require_relative 'config/environment'
Rails.application.eager_load!
models = Organizations::Users::TransferService.migratable_models
models_with_index = []
models_without_index = []
models.sort_by(&:name).each do |model|
user_assoc = model.reflect_on_all_associations.find { |assoc| assoc.class_name == 'User' }
next unless user_assoc
foreign_key = user_assoc.foreign_key
table_name = model.table_name
indices = model.connection.indexes(table_name).select { |idx| idx.columns.first == foreign_key }
model_info = {
name: model.name,
table: table_name,
assoc_name: user_assoc.name,
foreign_key: foreign_key,
indices: indices
}
if indices.any?
models_with_index << model_info
else
models_without_index << model_info
end
end
puts '=' * 80
puts 'SUMMARY'
puts '=' * 80
puts "Total migratable models: #{models.size}"
puts "Models WITH index on user association: #{models_with_index.size}"
puts "Models WITHOUT index on user association: #{models_without_index.size}"
puts
puts '=' * 80
puts 'MODELS WITH INDEX ON USER ASSOCIATION'
puts '=' * 80
models_with_index.each do |info|
puts "\n#{info[:name]}"
puts " Table: #{info[:table]}"
puts " User Association: #{info[:assoc_name]} (#{info[:foreign_key]})"
puts " Indices:"
info[:indices].each do |idx|
puts " - #{idx.name} on (#{idx.columns.join(', ')})#{idx.unique ? ' [UNIQUE]' : ''}"
end
end
puts
puts '=' * 80
puts 'MODELS WITHOUT INDEX ON USER ASSOCIATION'
puts '=' * 80
if models_without_index.empty?
puts 'None! All models have indices.'
else
models_without_index.each do |info|
puts "\n#{info[:name]}"
puts " Table: #{info[:table]}"
puts " User Association: #{info[:assoc_name]} (#{info[:foreign_key]})"
puts " ⚠️ NO INDEX FOUND"
end
end
All affected models have an index on the User foreign key:
Models with index on user association
================================================================================
SUMMARY
================================================================================
Total migratable models: 24
Models WITH index on user association: 24
Models WITHOUT index on user association: 0
================================================================================
MODELS WITH INDEX ON USER ASSOCIATION
================================================================================
Ai::Catalog::ItemVersion
Table: ai_catalog_item_versions
User Association: created_by (created_by_id)
Indices:
- index_ai_catalog_item_versions_on_created_by_id on (created_by_id)
Ai::Conversation::Thread
Table: ai_conversation_threads
User Association: user (user_id)
Indices:
- index_ai_conversation_threads_on_user_id_and_last_updated_at on (user_id, last_updated_at)
Ai::EventsCount
Table: ai_events_counts
User Association: user (user_id)
Indices:
- index_ai_events_counts_on_user_id on (user_id)
Ai::UsageEvent
Table: ai_usage_events
User Association: user (user_id)
Indices:
- index_ai_usage_events_on_user_id on (user_id)
Analytics::CustomDashboards::DashboardVersion
Table: custom_dashboard_versions
User Association: updated_by (updated_by_id)
Indices:
- index_custom_dashboard_versions_on_updated_by_id on (updated_by_id)
AuthenticationEvent
Table: authentication_events
User Association: user (user_id)
Indices:
- index_authentication_events_on_user_and_ip_address_and_result on (user_id, ip_address, result)
- index_successful_authentication_events_for_metrics on (user_id, provider, created_at)
AwardEmoji
Table: award_emoji
User Association: user (user_id)
Indices:
- idx_award_emoji_on_user_emoji_name_awardable_type_awardable_id on (user_id, name, awardable_type, awardable_id)
BulkImport
Table: bulk_imports
User Association: user (user_id)
Indices:
- index_bulk_imports_on_user_id on (user_id)
Dependencies::DependencyListExport
Table: dependency_list_exports
User Association: author (user_id)
Indices:
- index_dependency_list_exports_on_user_id on (user_id)
DeployKey
Table: keys
User Association: user (user_id)
Indices:
- index_keys_on_user_id on (user_id)
Doorkeeper::AccessToken
Table: oauth_access_tokens
User Association: resource_owner (resource_owner_id)
Indices:
- partial_index_user_id_app_id_created_at_token_not_revoked on (resource_owner_id, application_id, created_at)
GitlabSubscriptions::SeatAssignment
Table: subscription_seat_assignments
User Association: user (user_id)
Indices:
- index_subscription_seat_assignments_on_user_id on (user_id)
Import::Offline::Export
Table: import_offline_exports
User Association: user (user_id)
Indices:
- index_import_offline_exports_on_user_id on (user_id)
Import::PlaceholderUserDetail
Table: import_placeholder_user_details
User Association: placeholder_user (placeholder_user_id)
Indices:
- index_import_placeholder_user_details_on_placeholder_user_id on (placeholder_user_id)
Key
Table: keys
User Association: user (user_id)
Indices:
- index_keys_on_user_id on (user_id)
LDAPKey
Table: keys
User Association: user (user_id)
Indices:
- index_keys_on_user_id on (user_id)
OauthAccessGrant
Table: oauth_access_grants
User Association: resource_owner (resource_owner_id)
Indices:
- index_oauth_access_grants_on_resource_owner_id on (resource_owner_id, application_id, created_at)
OauthAccessToken
Table: oauth_access_tokens
User Association: resource_owner (resource_owner_id)
Indices:
- partial_index_user_id_app_id_created_at_token_not_revoked on (resource_owner_id, application_id, created_at)
PersonalAccessToken
Table: personal_access_tokens
User Association: user (user_id)
Indices:
- index_pat_on_user_id_and_expires_at on (user_id, expires_at)
- index_personal_access_tokens_on_user_id_and_id on (user_id, id)
PersonalSnippet
Table: snippets
User Association: author (author_id)
Indices:
- index_snippets_on_author_id on (author_id)
RemoteDevelopment::OrganizationClusterAgentMapping
Table: organization_cluster_agent_mappings
User Association: user (creator_id)
Indices:
- i_organization_cluster_agent_mappings_on_creator_id on (creator_id)
ResourceEvents::AbuseReportEvent
Table: abuse_report_events
User Association: user (user_id)
Indices:
- index_abuse_report_events_on_user_id on (user_id)
SpamLog
Table: spam_logs
User Association: user (user_id)
Indices:
- index_spam_logs_on_user_id on (user_id)
Vulnerabilities::Export
Table: vulnerability_exports
User Association: author (author_id)
Indices:
- index_vulnerability_exports_on_author_id on (author_id)
================================================================================
MODELS WITHOUT INDEX ON USER ASSOCIATION
================================================================================
None! All models have indices.
References
Relates to gitlab-org#19841 (closed)
Screenshots or screen recordings
| Before | After |
|---|---|
How to set up and validate locally
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 tim mccarthy