From 4ee4089e4448f642886c41dbafa934752e277c76 Mon Sep 17 00:00:00 2001 From: Shubham Kumar Date: Wed, 17 Dec 2025 14:00:56 +0100 Subject: [PATCH 1/6] Make Pages::DeletePagesDeploymentWorker compatible with group archival Changelog: added --- .../delete_group_pages_deployments_worker.rb | 39 ++++++++ lib/gitlab/event_store.rb | 1 + ...ete_group_pages_deployments_worker_spec.rb | 92 +++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 app/workers/pages/delete_group_pages_deployments_worker.rb create mode 100644 spec/workers/pages/delete_group_pages_deployments_worker_spec.rb diff --git a/app/workers/pages/delete_group_pages_deployments_worker.rb b/app/workers/pages/delete_group_pages_deployments_worker.rb new file mode 100644 index 00000000000000..b29a6a48d187a2 --- /dev/null +++ b/app/workers/pages/delete_group_pages_deployments_worker.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Pages + class DeleteGroupPagesDeploymentsWorker + include Gitlab::EventStore::Subscriber + + data_consistency :always + feature_category :pages + idempotent! + + def handle_event(event) + group_id = event.data[:group_id] + return unless group_id + + group = Group.find_by_id(group_id) + return unless group + + cursor = { current_id: group_id, depth: [group_id] } + iterator = Gitlab::Database::NamespaceEachBatch.new(namespace_class: Namespace, cursor: cursor) + + iterator.each_batch(of: 100) do |namespace_ids, _new_cursor| + project_namespace_ids = Namespaces::ProjectNamespace.id_in(namespace_ids) + + projects_with_pages(project_namespace_ids).each do |project| + user = project.owner + next unless user + + ::Pages::DeleteService.new(project, user).execute + end + end + end + + private + + def projects_with_pages(project_namespace_ids) + Project.by_project_namespace(project_namespace_ids).with_pages_deployed + end + end +end diff --git a/lib/gitlab/event_store.rb b/lib/gitlab/event_store.rb index dfc569d398f4df..59a51f19675da3 100644 --- a/lib/gitlab/event_store.rb +++ b/lib/gitlab/event_store.rb @@ -63,6 +63,7 @@ def configure!(store) if: ->(event) { ::Ml::ExperimentTracking::AssociateMlCandidateToPackageWorker.handles_event?(event) } store.subscribe ::Ci::InitializePipelinesIidSequenceWorker, to: ::Projects::ProjectCreatedEvent store.subscribe ::Pages::DeletePagesDeploymentWorker, to: ::Projects::ProjectArchivedEvent + store.subscribe ::Pages::DeleteGroupPagesDeploymentsWorker, to: ::Namespaces::Groups::GroupArchivedEvent store.subscribe ::Pages::ResetPagesDefaultDomainRedirectWorker, to: ::Pages::Domains::PagesDomainDeletedEvent store.subscribe ::MergeRequests::ProcessDraftNotePublishedWorker, to: ::MergeRequests::DraftNotePublishedEvent diff --git a/spec/workers/pages/delete_group_pages_deployments_worker_spec.rb b/spec/workers/pages/delete_group_pages_deployments_worker_spec.rb new file mode 100644 index 00000000000000..69b7884743ce7e --- /dev/null +++ b/spec/workers/pages/delete_group_pages_deployments_worker_spec.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Pages::DeleteGroupPagesDeploymentsWorker, feature_category: :pages do + let_it_be(:group) { create(:group) } + let_it_be(:subgroup) { create(:group, parent: group) } + let_it_be(:project_1) { create(:project, group: group) } + let_it_be(:project_2) { create(:project, group: group) } + let_it_be(:project_3) { create(:project, group: subgroup) } + let_it_be(:project_without_pages) { create(:project, group: group) } + + let!(:pages_deployment_1) { create(:pages_deployment, project: project_1) } + let!(:pages_deployment_2) { create(:pages_deployment, project: project_2) } + let!(:pages_deployment_3) { create(:pages_deployment, project: project_3) } + + let(:event) do + ::Namespaces::Groups::GroupArchivedEvent.new(data: { + group_id: group.id, + root_namespace_id: group.id + }) + end + + it_behaves_like 'worker with data consistency', described_class, data_consistency: :always + it_behaves_like 'subscribes to event' + + subject(:use_event) { consume_event(subscriber: described_class, event: event) } + + describe '#handle_event' do + context 'when group has multiple projects with pages' do + it 'calls Pages::DeleteService for each project with pages', :aggregate_failures do + expect(Pages::DeleteService).to receive(:new).with(project_1, project_1.owner).and_call_original + expect(Pages::DeleteService).to receive(:new).with(project_2, project_2.owner).and_call_original + expect(Pages::DeleteService).to receive(:new).with(project_3, project_3.owner).and_call_original + expect(Pages::DeleteService).not_to receive(:new).with(project_without_pages, anything) + + use_event + end + + it 'marks pages as not deployed for all projects', :sidekiq_inline do + project_1.update!(archived: true) + project_2.update!(archived: true) + project_3.update!(archived: true) + + expect { use_event } + .to change { project_1.reload.pages_deployed? }.from(true).to(false) + .and change { project_2.reload.pages_deployed? }.from(true).to(false) + .and change { project_3.reload.pages_deployed? }.from(true).to(false) + end + + it 'removes pages deployments for all projects in the group and subgroups', :sidekiq_inline do + project_1.update!(archived: true) + project_2.update!(archived: true) + project_3.update!(archived: true) + + expect { use_event } + .to change { PagesDeployment.count }.by(-3) + end + end + + context 'when group does not exist' do + let(:event) do + ::Namespaces::Groups::GroupArchivedEvent.new(data: { + group_id: non_existing_record_id, + root_namespace_id: non_existing_record_id + }) + end + + it 'does not raise an error' do + expect { use_event }.not_to raise_error + end + + it 'does not delete any pages deployments' do + expect { use_event }.not_to change { PagesDeployment.count } + end + end + + context 'when a project does not have an owner' do + before do + allow_next_found_instance_of(Project) do |project| + allow(project).to receive(:owner).and_return(nil) + end + end + + it 'skips that project' do + expect(Pages::DeleteService).not_to receive(:new) + + use_event + end + end + end +end -- GitLab From 08eb75513dd8f4814957f29be9cff838cf2b7242 Mon Sep 17 00:00:00 2001 From: Shubham Kumar Date: Wed, 17 Dec 2025 14:04:28 +0100 Subject: [PATCH 2/6] Update sidekiq config Changelog: fixed --- app/workers/all_queues.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 88c2579bdfff04..15e50e238b8b20 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -4784,6 +4784,16 @@ :idempotent: true :tags: [] :queue_namespace: +- :name: pages_delete_group_pages_deployments + :worker_name: Pages::DeleteGroupPagesDeploymentsWorker + :feature_category: :pages + :has_external_dependencies: false + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] + :queue_namespace: - :name: pages_delete_pages_deployment :worker_name: Pages::DeletePagesDeploymentWorker :feature_category: :pages -- GitLab From e89e7b6c201566e07b2037fc2fc6b1a924aad754 Mon Sep 17 00:00:00 2001 From: Shubham Kumar Date: Wed, 17 Dec 2025 14:13:18 +0100 Subject: [PATCH 3/6] Fix sidekiq config file Changelog: fixed --- config/sidekiq_queues.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 0ddc86629373dc..92bebeeaf8c14d 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -757,6 +757,8 @@ - 1 - - pages_delete_pages_deployment - 1 +- - pages_delete_group_pages_deployments + - 1 - - pages_domain_ssl_renewal - 1 - - pages_domain_verification -- GitLab From c730595061af8ef86b2a562e8f097b432b2d6c0c Mon Sep 17 00:00:00 2001 From: Shubham Kumar Date: Wed, 17 Dec 2025 14:16:00 +0100 Subject: [PATCH 4/6] Fix workers config Changelog: fixed --- config/sidekiq_queues.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 92bebeeaf8c14d..c69dba1c47747b 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -755,10 +755,10 @@ - 1 - - pages_deactivate_mr_deployments - 1 -- - pages_delete_pages_deployment - - 1 - - pages_delete_group_pages_deployments - 1 +- - pages_delete_pages_deployment + - 1 - - pages_domain_ssl_renewal - 1 - - pages_domain_verification -- GitLab From c08b6687866fa3c3ea8987c1ed99582b654b1e0e Mon Sep 17 00:00:00 2001 From: Shubham Kumar Date: Thu, 18 Dec 2025 03:30:59 +0100 Subject: [PATCH 5/6] Refactor group pages deploy Changelog: fixed --- .../pages/delete_group_pages_deployments_worker.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/workers/pages/delete_group_pages_deployments_worker.rb b/app/workers/pages/delete_group_pages_deployments_worker.rb index b29a6a48d187a2..d29b9000ba3827 100644 --- a/app/workers/pages/delete_group_pages_deployments_worker.rb +++ b/app/workers/pages/delete_group_pages_deployments_worker.rb @@ -19,9 +19,9 @@ def handle_event(event) iterator = Gitlab::Database::NamespaceEachBatch.new(namespace_class: Namespace, cursor: cursor) iterator.each_batch(of: 100) do |namespace_ids, _new_cursor| - project_namespace_ids = Namespaces::ProjectNamespace.id_in(namespace_ids) + project_namespaces = Namespaces::ProjectNamespace.id_in(namespace_ids) - projects_with_pages(project_namespace_ids).each do |project| + projects_with_pages(project_namespaces).each do |project| user = project.owner next unless user @@ -32,8 +32,8 @@ def handle_event(event) private - def projects_with_pages(project_namespace_ids) - Project.by_project_namespace(project_namespace_ids).with_pages_deployed + def projects_with_pages(project_namespaces) + Project.by_project_namespace(project_namespaces).with_pages_deployed end end end -- GitLab From b7b99c32dda1c136a839bf9ba299e25f6006b41e Mon Sep 17 00:00:00 2001 From: Shubham Kumar Date: Thu, 18 Dec 2025 09:51:01 +0100 Subject: [PATCH 6/6] Make undercoverage happy Changelog: fixed --- .../delete_group_pages_deployments_worker_spec.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/workers/pages/delete_group_pages_deployments_worker_spec.rb b/spec/workers/pages/delete_group_pages_deployments_worker_spec.rb index 69b7884743ce7e..4f544d73a6b382 100644 --- a/spec/workers/pages/delete_group_pages_deployments_worker_spec.rb +++ b/spec/workers/pages/delete_group_pages_deployments_worker_spec.rb @@ -88,5 +88,16 @@ use_event end end + + context 'when group_id is missing from event data' do + it 'returns early without processing' do + event = instance_double(Namespaces::Groups::GroupArchivedEvent, data: {}) + + expect(Group).not_to receive(:find_by_id) + expect(Pages::DeleteService).not_to receive(:new) + + described_class.new.handle_event(event) + end + end end end -- GitLab