From 7a90836369b4fe1b1f0ee139b68b96d7403d57cb Mon Sep 17 00:00:00 2001
From: John Mason <9717668-johnmason@users.noreply.gitlab.com>
Date: Wed, 16 Oct 2024 19:06:28 +0000
Subject: [PATCH] Add feature flag for zoekt index circuit breakers

Until we have index level eviction logic, we don't want to skip indexing
when an index watermark is exceeded.

Changelog: changed
EE: true
---
 .../search/zoekt/indexing_task_service.rb     | 22 +++++++++++------
 .../ops/zoekt_index_circuit_breaker.yml       |  9 +++++++
 .../zoekt/indexing_task_service_spec.rb       | 24 +++++++++++++++++++
 3 files changed, 48 insertions(+), 7 deletions(-)
 create mode 100644 ee/config/feature_flags/ops/zoekt_index_circuit_breaker.yml

diff --git a/ee/app/services/search/zoekt/indexing_task_service.rb b/ee/app/services/search/zoekt/indexing_task_service.rb
index f6080c11e0393..ace02b490f67d 100644
--- a/ee/app/services/search/zoekt/indexing_task_service.rb
+++ b/ee/app/services/search/zoekt/indexing_task_service.rb
@@ -28,13 +28,7 @@ def execute
 
         current_task_type = random_force_reindexing? ? :force_index_repo : task_type
         Router.fetch_indices_for_indexing(project_id, root_namespace_id: root_namespace_id).find_each do |idx|
-          # Note: we skip indexing tasks depending on storage watermark levels.
-          #
-          # If the low watermark is exceeded, we don't allow any new initial indexing tasks through,
-          # but we permit incremental indexing or force reindexing for existing repos.
-          #
-          # If the high watermark is exceeded, we don't allow any indexing tasks at all anymore.
-          if idx.high_watermark_exceeded? || (idx.low_watermark_exceeded? && initial_indexing?)
+          if index_circuit_breaker_enabled? && index_circuit_broken?(idx)
             IndexingTaskWorker.perform_in(WATERMARK_RESCHEDULE_INTERVAL, project_id, task_type, { index_id: idx.id })
             logger.info(
               build_structured_payload(
@@ -92,6 +86,20 @@ def random_force_reindexing?
       def eligible_for_force_reindexing?
         task_type == :index_repo && Feature.enabled?(:zoekt_random_force_reindexing, project, type: :ops)
       end
+
+      def index_circuit_broken?(idx)
+        # Note: we skip indexing tasks depending on storage watermark levels.
+        #
+        # If the low watermark is exceeded, we don't allow any new initial indexing tasks through,
+        # but we permit incremental indexing or force reindexing for existing repos.
+        #
+        # If the high watermark is exceeded, we don't allow any indexing tasks at all anymore.
+        idx.high_watermark_exceeded? || (idx.low_watermark_exceeded? && initial_indexing?)
+      end
+
+      def index_circuit_breaker_enabled?
+        Feature.enabled?(:zoekt_index_circuit_breaker, ::Project.actor_from_id(project_id), type: :ops)
+      end
     end
   end
 end
diff --git a/ee/config/feature_flags/ops/zoekt_index_circuit_breaker.yml b/ee/config/feature_flags/ops/zoekt_index_circuit_breaker.yml
new file mode 100644
index 0000000000000..6a612ec0f27d3
--- /dev/null
+++ b/ee/config/feature_flags/ops/zoekt_index_circuit_breaker.yml
@@ -0,0 +1,9 @@
+---
+name: zoekt_index_circuit_breaker
+feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/494260
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/168566
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/498316
+milestone: '17.5'
+group: group::global search
+type: ops
+default_enabled: false
diff --git a/ee/spec/services/search/zoekt/indexing_task_service_spec.rb b/ee/spec/services/search/zoekt/indexing_task_service_spec.rb
index 3c8e7ad3ee001..7fed1356508c9 100644
--- a/ee/spec/services/search/zoekt/indexing_task_service_spec.rb
+++ b/ee/spec/services/search/zoekt/indexing_task_service_spec.rb
@@ -20,6 +20,18 @@
   end
 
   describe '#execute' do
+    RSpec.shared_examples 'creates a task when circuit breaker is disabled' do
+      context 'with index circuit breaker feature flag disabled' do
+        before do
+          stub_feature_flags(zoekt_index_circuit_breaker: false)
+        end
+
+        it 'creates Search::Zoekt::Task record' do
+          expect { service.execute }.to change { Search::Zoekt::Task.count }.by(1)
+        end
+      end
+    end
+
     context 'when a watermark is exceeded' do
       let(:service) { described_class.new(project.id, task_type) }
       let(:task_type) { :index_repo }
@@ -49,6 +61,8 @@
 
             service.execute
           end
+
+          it_behaves_like 'creates a task when circuit breaker is disabled'
         end
 
         context 'with force reindexing' do
@@ -67,6 +81,8 @@
 
               service.execute
             end
+
+            it_behaves_like 'creates a task when circuit breaker is disabled'
           end
 
           context 'when a repo already exists' do
@@ -75,6 +91,8 @@
               create(:zoekt_repository, project: project, zoekt_index: zoekt_index, state: repo_state)
             end
 
+            it_behaves_like 'creates a task when circuit breaker is disabled'
+
             context 'and is ready' do
               let_it_be(:repo_state) { ::Search::Zoekt::Repository.states.fetch(:ready) }
 
@@ -90,6 +108,8 @@
 
                 service.execute
               end
+
+              it_behaves_like 'creates a task when circuit breaker is disabled'
             end
 
             context 'and is not ready' do
@@ -107,6 +127,8 @@
 
                 service.execute
               end
+
+              it_behaves_like 'creates a task when circuit breaker is disabled'
             end
           end
         end
@@ -131,6 +153,8 @@
         it 'does not create Search::Zoekt::Task record' do
           expect { service.execute }.not_to change { Search::Zoekt::Task.count }
         end
+
+        it_behaves_like 'creates a task when circuit breaker is disabled'
       end
     end
 
-- 
GitLab