diff --git a/db/post_migrate/20220628012902_finalise_project_namespace_members.rb b/db/post_migrate/20220628012902_finalise_project_namespace_members.rb
new file mode 100644
index 0000000000000000000000000000000000000000..29b11fb4357a8d120b12578727ab628a104dd229
--- /dev/null
+++ b/db/post_migrate/20220628012902_finalise_project_namespace_members.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class FinaliseProjectNamespaceMembers < Gitlab::Database::Migration[2.0]
+  MIGRATION = 'BackfillProjectMemberNamespaceId'
+  disable_ddl_transaction!
+
+  restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+  def up
+    ensure_batched_background_migration_is_finished(
+      job_class_name: MIGRATION,
+      table_name: :members,
+      column_name: :id,
+      job_arguments: [],
+      finalize: true
+    )
+  end
+
+  def down
+    # no-op
+  end
+end
diff --git a/db/schema_migrations/20220628012902 b/db/schema_migrations/20220628012902
new file mode 100644
index 0000000000000000000000000000000000000000..ef7325629cab356353334481f8369ef4fc4e51d3
--- /dev/null
+++ b/db/schema_migrations/20220628012902
@@ -0,0 +1 @@
+5881441f8a6c0f25cff00aa9e164a1c19bcc34d4db678fc50712824fff82b24e
\ No newline at end of file
diff --git a/spec/migrations/20220628012902_finalise_project_namespace_members_spec.rb b/spec/migrations/20220628012902_finalise_project_namespace_members_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1f116cf6a7efd86fb511b8c4292f4087227ab4b7
--- /dev/null
+++ b/spec/migrations/20220628012902_finalise_project_namespace_members_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe FinaliseProjectNamespaceMembers, :migration do
+  let(:batched_migrations) { table(:batched_background_migrations) }
+
+  let_it_be(:migration) { described_class::MIGRATION }
+
+  describe '#up' do
+    shared_examples 'finalizes the migration' do
+      it 'finalizes the migration' do
+        allow_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |runner|
+          expect(runner).to receive(:finalize).with('BackfillProjectMemberNamespaceId', :members, :id, [])
+        end
+      end
+    end
+
+    context 'when migration is missing' do
+      it 'warns migration not found' do
+        expect(Gitlab::AppLogger)
+          .to receive(:warn).with(/Could not find batched background migration for the given configuration:/)
+
+        migrate!
+      end
+    end
+
+    context 'with migration present' do
+      let!(:project_member_namespace_id_backfill) do
+        batched_migrations.create!(
+          job_class_name: 'BackfillProjectMemberNamespaceId',
+          table_name: :members,
+          column_name: :id,
+          job_arguments: [],
+          interval: 2.minutes,
+          min_value: 1,
+          max_value: 2,
+          batch_size: 1000,
+          sub_batch_size: 200,
+          gitlab_schema: :gitlab_main,
+          status: 3 # finished
+        )
+      end
+
+      context 'when migration finished successfully' do
+        it 'does not raise exception' do
+          expect { migrate! }.not_to raise_error
+        end
+      end
+
+      context 'with different migration statuses' do
+        using RSpec::Parameterized::TableSyntax
+
+        where(:status, :description) do
+          0 | 'paused'
+          1 | 'active'
+          4 | 'failed'
+          5 | 'finalizing'
+        end
+
+        with_them do
+          before do
+            project_member_namespace_id_backfill.update!(status: status)
+          end
+
+          it_behaves_like 'finalizes the migration'
+        end
+      end
+    end
+  end
+end