diff --git a/db/docs/batched_background_migrations/backfill_default_organization_users.yml b/db/docs/batched_background_migrations/backfill_default_organization_users.yml new file mode 100644 index 0000000000000000000000000000000000000000..e22af38e1c43f0f23d0255b8d0149fd7fd1e5ac1 --- /dev/null +++ b/db/docs/batched_background_migrations/backfill_default_organization_users.yml @@ -0,0 +1,10 @@ +--- +migration_job_name: BackfillDefaultOrganizationUsers +description: Populates organization_users with regular users (non admins) to the default organization. +feature_category: cell +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144674 +milestone: '16.10' +queued_migration_version: 20240213210124 +# Replace with the approximate date you think it's best to ensure the completion of this BBM. +finalize_after: '2024-03-15' +finalized_by: # version of the migration that finalized this BBM diff --git a/db/post_migrate/20240213210124_queue_backfill_default_organization_users.rb b/db/post_migrate/20240213210124_queue_backfill_default_organization_users.rb new file mode 100644 index 0000000000000000000000000000000000000000..35a9387a35fbb4ee0e9f092adb4d30239720327a --- /dev/null +++ b/db/post_migrate/20240213210124_queue_backfill_default_organization_users.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class QueueBackfillDefaultOrganizationUsers < Gitlab::Database::Migration[2.2] + milestone '16.10' + + MIGRATION = "BackfillDefaultOrganizationUsers" + DELAY_INTERVAL = 2.minutes + BATCH_SIZE = 3_000 + SUB_BATCH_SIZE = 250 + MAX_BATCH_SIZE = 10_000 + + restrict_gitlab_migration gitlab_schema: :gitlab_main + + def up + queue_batched_background_migration( + MIGRATION, + :users, + :id, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE, + max_batch_size: MAX_BATCH_SIZE + ) + end + + def down + delete_batched_background_migration(MIGRATION, :users, :id, []) + end +end diff --git a/db/schema_migrations/20240213210124 b/db/schema_migrations/20240213210124 new file mode 100644 index 0000000000000000000000000000000000000000..946ee93f61202792e5d1592e16ba022086f4ef55 --- /dev/null +++ b/db/schema_migrations/20240213210124 @@ -0,0 +1 @@ +71f7ea5193599d6aabe52d2816505628d25f71f3ea7faf4560e7e53e1bf6c067 \ No newline at end of file diff --git a/lib/gitlab/background_migration/backfill_default_organization_users.rb b/lib/gitlab/background_migration/backfill_default_organization_users.rb new file mode 100644 index 0000000000000000000000000000000000000000..bd51ce52e49cc4fe84d3f39a4a824bb8b1282ab8 --- /dev/null +++ b/lib/gitlab/background_migration/backfill_default_organization_users.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + class BackfillDefaultOrganizationUsers < BatchedMigrationJob + operation_name :backfill_default_organization_users # This is used as the key on collecting metrics + scope_to ->(relation) { relation.where(admin: false) } # true handled in BackfillDefaultOrganizationOwnersAgain + feature_category :cell + + DEFAULT_ACCESS_LEVEL = 10 + DEFAULT_ORGANIZATION_ID = 1 + + module Organizations + class OrganizationUser < ApplicationRecord + self.table_name = 'organization_users' + self.inheritance_column = :_type_disabled + end + end + + def perform + each_sub_batch do |sub_batch| + organization_users_attributes = sub_batch.select(:id).map do |user| + { + user_id: user.id, + organization_id: DEFAULT_ORGANIZATION_ID, + access_level: DEFAULT_ACCESS_LEVEL + } + end + + Organizations::OrganizationUser.upsert_all( + organization_users_attributes, + returning: false, + unique_by: [:organization_id, :user_id] + ) + end + end + end + end +end diff --git a/spec/lib/gitlab/background_migration/backfill_default_organization_users_spec.rb b/spec/lib/gitlab/background_migration/backfill_default_organization_users_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..b0f67f4f8e1963fa0e990fd8cef660444441f0b2 --- /dev/null +++ b/spec/lib/gitlab/background_migration/backfill_default_organization_users_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::BackfillDefaultOrganizationUsers, feature_category: :cell do + let(:organization_users) { table(:organization_users) } + let(:users) { table(:users) } + + let!(:first_user) { users.create!(name: 'first', email: 'first_user@example.com', projects_limit: 1) } + let!(:last_user) { users.create!(name: 'last', email: 'last_user@example.com', projects_limit: 1) } + let!(:admin) { users.create!(name: 'admin user', email: 'admin_user@example.com', projects_limit: 1, admin: true) } + + subject(:migration) do + described_class.new( + start_id: first_user.id, + end_id: admin.id, + batch_table: :users, + batch_column: :id, + sub_batch_size: 100, + pause_ms: 0, + connection: ApplicationRecord.connection + ) + end + + describe '#perform' do + context 'with no entries for a regular user in organization_users' do + it 'adds regular users correctly with the default organization to organization_users' do + expect(organization_users.count).to eq(0) + + expect { migration.perform }.to change { organization_users.count }.by(2) + + expect(organization_user_as_regular_user_exists?(first_user.id)).to be(true) + expect(organization_user_as_regular_user_exists?(last_user.id)).to be(true) + end + end + + context 'when user already exists in organization_users as an admin user' do + before do + organization_users.create!( + organization_id: Organizations::Organization::DEFAULT_ORGANIZATION_ID, + user_id: first_user.id, + access_level: Gitlab::Access::OWNER + ) + end + + it 'updates the organization_users entry to a regular user' do + expect(organization_users.count).to eq(1) + + expect { migration.perform }.to change { organization_users.count }.by(1) + + expect(organization_user_as_regular_user_exists?(first_user.id)).to be(true) + expect(organization_user_as_regular_user_exists?(last_user.id)).to be(true) + end + end + end + + def organization_user_as_regular_user_exists?(user_id) + organization_users.exists?( + organization_id: Organizations::Organization::DEFAULT_ORGANIZATION_ID, + user_id: user_id, + access_level: Gitlab::Access::GUEST + ) + end +end diff --git a/spec/migrations/20240213210124_queue_backfill_default_organization_users_spec.rb b/spec/migrations/20240213210124_queue_backfill_default_organization_users_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..0ebcea6f4545691e819e98a3b2edd0bf31189b6d --- /dev/null +++ b/spec/migrations/20240213210124_queue_backfill_default_organization_users_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe QueueBackfillDefaultOrganizationUsers, feature_category: :cell do + let!(:batched_migration) { described_class::MIGRATION } + + it 'schedules a new batched migration' do + reversible_migration do |migration| + migration.before -> { + expect(batched_migration).not_to have_scheduled_batched_migration + } + + migration.after -> { + expect(batched_migration).to have_scheduled_batched_migration( + table_name: :users, + column_name: :id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE, + max_batch_size: described_class::MAX_BATCH_SIZE + ) + } + end + end +end