Remove join_field from main Advanced Search index documents
What does this MR do and why?
Adds an Advanced Search migration, RemoveJoinFieldFromMainIndex, that strips the unused join_field from stored documents in the main index. This is task 4 of #630682.
Nothing reads join_field anymore (!259063 (merged)), and Rails no longer writes it (!259064 (merged)). The main index mapping is dynamic: 'strict', so when the reindex in #600600 drops the field from the mapping, any document that still has it fails to copy and the reindex aborts. This migration removes the field from those documents first.
Merge only after the indexer bump. gitlab-elasticsearch-indexer stops writing join_field on blobs in gitlab-elasticsearch-indexer!947 (merged), which ships in v5.14.18 (gitlab-elasticsearch-indexer!960 (closed)). Master still runs v5.14.17. If this migration runs before GITLAB_ELASTICSEARCH_INDEXER_VERSION is 5.14.18, every blob the indexer rewrites afterwards gets join_field back.
Advanced Search migration
batched!,batch_size 10_000,throttle_delay 1.minute,retry_on_failure.- Each batch runs
update_by_queryon the main index alias with anexistsquery onjoin_fieldandctx._source.remove('join_field'). completed?refreshes the index and counts documents that still havejoin_field. The migration is done when the count is 0.- All document types. It doesn't filter by type, because
join_fieldis on both blob and milestone documents. - Why not
MigrationRemoveFieldsHelper. The helper filters by one document type, which it builds from a model class (DOCUMENT_TYPE.name.underscore). Blob documents have no model class, so it can't target them. Itsupdate_by_queryalso has noconflicts: 'proceed'. Making it work would mean overridingquery,completed?andupdate_by_query!, which is nearly the whole helper. - Version conflicts. It uses
conflicts: 'proceed'. The indexer rewrites blobs while this runs, and a conflicting document is being reindexed anyway. Any document still left is picked up by a later batch, becausecompleted?counts what remains. - Routing.
update_by_querykeeps each document's routing (project_<id>). The spec checks that milestones can still be fetched with their project routing afterwards. - Reversibility. It only removes a field that nothing reads. It deletes no documents, and it's safe to run again.
Sizing
Locally, join_field is on 18,147 blob and 85 milestone documents (all of them, out of 18,253 in the main index). The production count hasn't been taken yet. That's also task 5 of #630682. At 10,000 documents a minute, 10 million documents take about 17 hours. If production has far more blob documents, the batch size can go up before merge.
References
Screenshots or screen recordings
Not applicable: no UI change.
How to set up and validate locally
-
Check how many main index documents have
join_field:curl -s "localhost:9200/gitlab-development/_count" -H 'Content-Type: application/json' \ -d '{"query":{"exists":{"field":"join_field"}}}' -
In a Rails console, load the migration (files in
ee/elastic/migratearen't autoloaded) and run it until it finishes:require Rails.root.join('ee/elastic/migrate/20261013143426_remove_join_field_from_main_index.rb').to_s migration = RemoveJoinFieldFromMainIndex.new(20261013143426) migration.migrate until migration.completed? -
Run the count again. It returns
0.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.