diff --git a/db/docs/batched_background_migrations/backfill_members_request_accepted_at.yml b/db/docs/batched_background_migrations/backfill_members_request_accepted_at.yml new file mode 100644 index 0000000000000000000000000000000000000000..1be05b8459f2b99c6e2e6c5571086afead1fe9e3 --- /dev/null +++ b/db/docs/batched_background_migrations/backfill_members_request_accepted_at.yml @@ -0,0 +1,8 @@ +--- +migration_job_name: BackfillMembersRequestAcceptedAt +description: Backfills `request_accepted_at` column in the `members` table to `created_at` column value for all existing records. +feature_category: groups_and_projects +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166812 +milestone: '17.5' +queued_migration_version: 20240920083708 +finalized_by: diff --git a/db/post_migrate/20240920083708_queue_backfill_members_request_accepted_at.rb b/db/post_migrate/20240920083708_queue_backfill_members_request_accepted_at.rb new file mode 100644 index 0000000000000000000000000000000000000000..f5f076ca646eea3bb55e2842f3098389cd70d82b --- /dev/null +++ b/db/post_migrate/20240920083708_queue_backfill_members_request_accepted_at.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class QueueBackfillMembersRequestAcceptedAt < Gitlab::Database::Migration[2.2] + milestone '17.5' + restrict_gitlab_migration gitlab_schema: :gitlab_main + + MIGRATION = "BackfillMembersRequestAcceptedAt" + DELAY_INTERVAL = 2.minutes + BATCH_SIZE = 1000 + SUB_BATCH_SIZE = 100 + + def up + queue_batched_background_migration( + MIGRATION, + :members, + :id, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE + ) + end + + def down + delete_batched_background_migration(MIGRATION, :members, :id, []) + end +end diff --git a/db/schema_migrations/20240920083708 b/db/schema_migrations/20240920083708 new file mode 100644 index 0000000000000000000000000000000000000000..48c0148798e55ba849adc2fedd066845715d756b --- /dev/null +++ b/db/schema_migrations/20240920083708 @@ -0,0 +1 @@ +487b9bcc614eef44330b4c5024b0b9383a6271981f436fda86a0d95b93689e70 \ No newline at end of file diff --git a/lib/gitlab/background_migration/backfill_members_request_accepted_at.rb b/lib/gitlab/background_migration/backfill_members_request_accepted_at.rb new file mode 100644 index 0000000000000000000000000000000000000000..d57d507cf02094ff106eceb8b67edd308efeb968 --- /dev/null +++ b/lib/gitlab/background_migration/backfill_members_request_accepted_at.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + class BackfillMembersRequestAcceptedAt < BatchedMigrationJob + operation_name :backfill_members_request_accepted_at + feature_category :groups_and_projects + + def perform + each_sub_batch do |sub_batch| + sub_batch + .where(requested_at: nil) + .where(invite_token: nil) + .where(invite_accepted_at: nil) + .where(request_accepted_at: nil) + .update_all("request_accepted_at = created_at") + end + end + end + end +end diff --git a/spec/lib/gitlab/background_migration/backfill_members_request_accepted_at_spec.rb b/spec/lib/gitlab/background_migration/backfill_members_request_accepted_at_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..ce983408bdd60e499281b2f2ec9215d16f20db3f --- /dev/null +++ b/spec/lib/gitlab/background_migration/backfill_members_request_accepted_at_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::BackfillMembersRequestAcceptedAt, schema: 20240920083708, feature_category: :groups_and_projects do + let!(:namespace) { table(:namespaces).create!({ name: "test-1", path: "test-1", owner_id: 1 }) } + let!(:member) { table(:members) } + let!(:member_data) do + { + access_level: ::Gitlab::Access::MAINTAINER, + member_namespace_id: namespace.id, + notification_level: 3, + source_type: "Namespace", + source_id: 22, + created_at: "2024-09-14 06:06:16.649264" + } + end + + let!(:member1) { member.create!(member_data) } + let!(:member2) { member.create!(member_data) } + let!(:member3) { member.create!(member_data.merge(requested_at: Time.current)) } + let!(:member4) { member.create!(member_data.merge(invite_token: 'token')) } + let!(:member5) { member.create!(member_data.merge(request_accepted_at: Time.current)) } + let!(:member6) { member.create!(member_data.merge(invite_accepted_at: Time.current)) } + + subject(:migration) do + described_class.new( + start_id: member1.id, + end_id: member6.id, + batch_table: :members, + batch_column: :id, + sub_batch_size: 100, + pause_ms: 0, + connection: ApplicationRecord.connection + ) + end + + describe '#perform' do + context 'when `requested_at`, `invite_token`, `invite_accepted_at` and `request_accepted_at` are set to nil' do + it 'backfills `request_accepted_at` column to `created_at` for eligible members' do + expect { migration.perform } + .to change { member1.reload.request_accepted_at }.from(nil).to(member1.created_at) + .and change { member2.reload.request_accepted_at }.from(nil).to(member2.created_at) + .and not_change { member3.reload.request_accepted_at } + .and not_change { member4.reload.request_accepted_at } + .and not_change { member5.reload.request_accepted_at } + .and not_change { member6.reload.request_accepted_at } + end + end + end +end diff --git a/spec/migrations/20240920083708_queue_backfill_members_request_accepted_at_spec.rb b/spec/migrations/20240920083708_queue_backfill_members_request_accepted_at_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..00235d0d4b1c1970070d8d690fd14376f911f722 --- /dev/null +++ b/spec/migrations/20240920083708_queue_backfill_members_request_accepted_at_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe QueueBackfillMembersRequestAcceptedAt, feature_category: :groups_and_projects 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: :members, + column_name: :id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE + ) + } + end + end +end