diff --git a/ee/app/workers/concerns/search/elastic/migration_database_backfill_helper.rb b/ee/app/workers/concerns/search/elastic/migration_database_backfill_helper.rb index c0830e211e9e64ca46941ce81c66f6d6e33ed6f4..9ccc1d85858c3934bed6e956f13f141d0f2b8454 100644 --- a/ee/app/workers/concerns/search/elastic/migration_database_backfill_helper.rb +++ b/ee/app/workers/concerns/search/elastic/migration_database_backfill_helper.rb @@ -16,13 +16,19 @@ def migrate end def completed? - maximum_id = documents_after_current_id.maximum(:id).to_i - documents_remaining_approximate = maximum_id - current_id - set_migration_state(maximum_id: maximum_id, documents_remaining_approximate: documents_remaining_approximate) - log 'Checking if migration is finished', maximum_id: maximum_id, current_id: current_id, - documents_remaining_approximate: documents_remaining_approximate + completed = documents_after_current_id.empty? - documents_after_current_id.empty? + unless completed + maximum_id = documents_after_current_id.maximum(:id).to_i + documents_remaining_approximate = maximum_id - current_id + + set_migration_state(maximum_id: maximum_id, documents_remaining_approximate: documents_remaining_approximate) + + log 'Migration is not finished', maximum_id: maximum_id, current_id: current_id, + documents_remaining_approximate: documents_remaining_approximate + end + + completed end def document_type @@ -49,7 +55,7 @@ def limit_per_iteration DEFAULT_LIMIT_PER_ITERATION end - def number_of_iteration_per_run + def number_of_iterations_per_run (batch_size / limit_per_iteration.to_f).ceil end @@ -63,7 +69,7 @@ def documents_after_current_id def backfill_documents [].tap do |documents| - number_of_iteration_per_run.times do + number_of_iterations_per_run.times do documents = documents_after_current_id.limit(limit_per_iteration) if limit_indexing? diff --git a/ee/elastic/docs/20240609110316_index_all_issues_from_database.yml b/ee/elastic/docs/20240609110316_index_all_issues_from_database.yml new file mode 100644 index 0000000000000000000000000000000000000000..5bb64e510f09d1d9318c7a63ac59c83b84792c2a --- /dev/null +++ b/ee/elastic/docs/20240609110316_index_all_issues_from_database.yml @@ -0,0 +1,10 @@ +--- +name: IndexAllIssuesFromDatabase +version: '20240609110316' +description: Indexes every issue from the database to ensure the correct state +group: group::global search +milestone: '17.3' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/158039 +obsolete: false +marked_obsolete_by_url: +marked_obsolete_in_milestone: diff --git a/ee/elastic/migrate/20240609110316_index_all_issues_from_database.rb b/ee/elastic/migrate/20240609110316_index_all_issues_from_database.rb new file mode 100644 index 0000000000000000000000000000000000000000..4533a9cd6b97f41eb2330d5d6b8d6eb2006989e8 --- /dev/null +++ b/ee/elastic/migrate/20240609110316_index_all_issues_from_database.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class IndexAllIssuesFromDatabase < Elastic::Migration + include ::Search::Elastic::MigrationDatabaseBackfillHelper + + batch_size 50_000 + batched! + throttle_delay 1.minute + retry_on_failure + + DOCUMENT_TYPE = Issue + + def respect_limited_indexing? + true + end + + def item_to_preload + :project + end + + # rubocop:disable CodeReuse/ActiveRecord -- we need to select only unprocessed ids + def documents_after_current_id + document_type.without_issue_type(:epic).where("issues.id > ?", current_id).order(:id) + end + # rubocop:enable CodeReuse/ActiveRecord +end diff --git a/ee/lib/elastic/latest/issue_instance_proxy.rb b/ee/lib/elastic/latest/issue_instance_proxy.rb index 3be726395d2d1115ba37a08c5081df8f109aa4d6..a0d7badec2d09ff3cdec125836e857f74d2a6231 100644 --- a/ee/lib/elastic/latest/issue_instance_proxy.rb +++ b/ee/lib/elastic/latest/issue_instance_proxy.rb @@ -3,7 +3,7 @@ module Elastic module Latest class IssueInstanceProxy < ApplicationInstanceProxy - SCHEMA_VERSION = 24_05 + SCHEMA_VERSION = 24_07 OPTIONAL_FIELDS = %w[embedding embedding_version].freeze def as_indexed_json(options = {}) diff --git a/ee/spec/elastic/migrate/20240609110316_index_all_issues_from_database_spec.rb b/ee/spec/elastic/migrate/20240609110316_index_all_issues_from_database_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..b165b2013e3a5b7242f7d28ed8beb816c6d2b292 --- /dev/null +++ b/ee/spec/elastic/migrate/20240609110316_index_all_issues_from_database_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' +require File.expand_path('ee/elastic/migrate/20240609110316_index_all_issues_from_database.rb') + +RSpec.describe IndexAllIssuesFromDatabase, feature_category: :global_search do + let(:version) { 20240609110316 } + + describe 'migration', :elastic do + it_behaves_like 'migration reindexes all data' do + let(:objects) { create_list(:issue, 3) } + let(:expected_throttle_delay) { 1.minute } + let(:expected_batch_size) { 50_000 } + end + end + + describe '.documents_after_current_id' do + let(:migration) { described_class.new(version) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:issue) { create(:issue, project: project) } + let_it_be(:issue_epic_type) { create(:issue, :epic) } + let_it_be(:issue_task_type) { create(:issue, :task) } + let_it_be(:work_item) { create(:work_item, :epic_with_legacy_epic, :group_level) } + let_it_be(:non_group_work_item) { create(:work_item) } + + it 'only indexes project-level work_item_type issues' do + expected_ids = [issue.id, issue_task_type.id, non_group_work_item.id] + expect(migration.documents_after_current_id.pluck(:id)).to match_array(expected_ids) + end + end +end diff --git a/ee/spec/support/shared_examples/elastic/migration_shared_examples.rb b/ee/spec/support/shared_examples/elastic/migration_shared_examples.rb index f8293a73c7492ea091473abed45ddd852b8448da..187c985a93d4bcce1bca8d715fd3bf7810317379 100644 --- a/ee/spec/support/shared_examples/elastic/migration_shared_examples.rb +++ b/ee/spec/support/shared_examples/elastic/migration_shared_examples.rb @@ -730,7 +730,6 @@ def update_by_query(objects, script) migration.migrate # To set a pristine state expect(migration.completed?).to be_truthy - expect(migration.migration_state).to match(task_id: nil, documents_remaining: 0) end context 'and task in progress' do