Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/changelog/144782.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
area: Infra/Core
issues:
- 144764
pr: 144782
summary: Fix system index mapping update for reindexed indices after migration
type: bug
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

package org.elasticsearch.indices;

import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
Expand Down Expand Up @@ -93,6 +95,40 @@ public void testSystemIndexManagerLeavesNewerMappingsAlone() throws Exception {
assertBusy(() -> assertMappingsAndSettings(TestSystemIndexDescriptor.getNewMappings()));
}

/**
* Reproduces a bug where system index mappings are not updated after a major-version reindex migration.
* <a href="https://github.com/elastic/elasticsearch/issues/144764"></a>
*/
public void testSystemIndexManagerUpgradesMappingsOfReindexedIndex() throws Exception {
internalCluster().startNodes(1);

// Directly set up the post-migration state: a reindexed concrete index with old mappings,
// and the original primary index name as an alias pointing to it.
// Use MIGRATE_SYSTEM_INDEX_CAUSE so that TransportCreateIndexAction allows creating a system
// index whose name is not the descriptor's primary index, mirroring SystemIndexMigrator.
final String reindexedIndexName = PRIMARY_INDEX_NAME + SystemIndices.UPGRADED_INDEX_SUFFIX;
assertAcked(
indicesAdmin().create(
new CreateIndexRequest(reindexedIndexName).cause(SystemIndices.MIGRATE_SYSTEM_INDEX_CAUSE)
.settings(TestSystemIndexDescriptor.SETTINGS)
.mapping(TestSystemIndexDescriptor.getOldMappings())
).actionGet()
);
ensureGreen(reindexedIndexName);
assertAcked(
indicesAdmin().aliases(
new IndicesAliasesRequest(TEST_REQUEST_TIMEOUT, TEST_REQUEST_TIMEOUT).addAliasAction(
IndicesAliasesRequest.AliasActions.add().index(reindexedIndexName).alias(PRIMARY_INDEX_NAME)
).addAliasAction(IndicesAliasesRequest.AliasActions.add().index(reindexedIndexName).alias(INDEX_NAME))
).actionGet()
);

TestSystemIndexDescriptor.useNewMappings.set(true);
triggerClusterStateUpdates();

assertBusy(() -> assertMappingsAndSettings(TestSystemIndexDescriptor.getNewMappings(), reindexedIndexName));
}

/**
* Ensures that we can clear any blocks that get set on managed system indices.
*
Expand All @@ -119,29 +155,33 @@ private void triggerClusterStateUpdates() {
indicesAdmin().putTemplate(new PutIndexTemplateRequest(name).patterns(List.of(name))).actionGet();
}

private void assertMappingsAndSettings(String expectedMappings) {
assertMappingsAndSettings(expectedMappings, PRIMARY_INDEX_NAME);
}

/**
* Fetch the mappings and settings for {@link TestSystemIndexDescriptor#INDEX_NAME} and verify that they match the expected values.
*/
private void assertMappingsAndSettings(String expectedMappings) {
private void assertMappingsAndSettings(String expectedMappings, String primaryIndexName) {
final GetMappingsResponse getMappingsResponse = indicesAdmin().getMappings(
new GetMappingsRequest(TEST_REQUEST_TIMEOUT).indices(INDEX_NAME)
).actionGet();

final Map<String, MappingMetadata> mappings = getMappingsResponse.getMappings();
assertThat(
"Expected mappings to contain a key for [" + PRIMARY_INDEX_NAME + "], but found: " + mappings.toString(),
mappings.containsKey(PRIMARY_INDEX_NAME),
"Expected mappings to contain a key for [" + primaryIndexName + "], but found: " + mappings.toString(),
mappings.containsKey(primaryIndexName),
equalTo(true)
);
final Map<String, Object> sourceAsMap = mappings.get(PRIMARY_INDEX_NAME).getSourceAsMap();
final Map<String, Object> sourceAsMap = mappings.get(primaryIndexName).getSourceAsMap();

assertThat(sourceAsMap, equalTo(XContentHelper.convertToMap(XContentType.JSON.xContent(), expectedMappings, false)));

final GetSettingsResponse getSettingsResponse = indicesAdmin().getSettings(
new GetSettingsRequest(TEST_REQUEST_TIMEOUT).indices(INDEX_NAME)
).actionGet();

final Settings actual = getSettingsResponse.getIndexToSettings().get(PRIMARY_INDEX_NAME);
final Settings actual = getSettingsResponse.getIndexToSettings().get(primaryIndexName);

for (String settingName : TestSystemIndexDescriptor.SETTINGS.keySet()) {
assertThat(actual.get(settingName), equalTo(TestSystemIndexDescriptor.SETTINGS.get(settingName)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.elasticsearch.cluster.ProjectState;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.health.ClusterIndexHealth;
import org.elasticsearch.cluster.metadata.IndexAbstraction;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.elasticsearch.cluster.metadata.ProjectId;
Expand Down Expand Up @@ -249,7 +250,7 @@ public void onFailure(Exception e) {
* @return a summary of the index state, or <code>null</code> if the index doesn't exist
*/
static State calculateIndexState(ProjectState state, SystemIndexDescriptor descriptor) {
final IndexMetadata indexMetadata = state.metadata().index(descriptor.getPrimaryIndex());
IndexMetadata indexMetadata = getSystemIndexMetadata(state, descriptor);

if (indexMetadata == null) {
return null;
Expand Down Expand Up @@ -278,6 +279,21 @@ static State calculateIndexState(ProjectState state, SystemIndexDescriptor descr
return new State(indexState, indexHealth, isIndexUpToDate, isMappingIsUpToDate);
}

private static IndexMetadata getSystemIndexMetadata(ProjectState state, SystemIndexDescriptor descriptor) {
String primaryIndexName = descriptor.getPrimaryIndex();
ProjectMetadata projectMetadata = state.metadata();
IndexMetadata indexMetadata = projectMetadata.index(primaryIndexName);
if (indexMetadata == null) {
// The primary index name might be an alias pointing to the concrete index
// (e.g. ".fleet-agents-7" → ".fleet-agents-7-reindexed-for-9").
IndexAbstraction indexAbstraction = projectMetadata.getIndicesLookup().get(primaryIndexName);
if (indexAbstraction != null && indexAbstraction.getWriteIndex() != null) {
indexMetadata = projectMetadata.getIndexSafe(indexAbstraction.getWriteIndex());
}
}
return indexMetadata;
}

/**
* Checks whether an index's mappings are up-to-date. If an index is encountered that has
* a version higher than Version.CURRENT, it is still considered up-to-date.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,42 @@ public void testManagerSubmitsPutRequest() {
verify(client, times(1)).execute(same(TransportPutMappingAction.TYPE), any(PutMappingRequest.class), any());
}

/**
* Reproduces a bug where SystemIndexMappingUpdateService incorrectly reports UP_TO_DATE for an index whose mappings are
* outdated after a major-version migration (reindex).
*/
public void testManagerDetectsOutdatedMappingsInReindexedIndex() {
final String reindexedIndexName = SYSTEM_INDEX_NAME + SystemIndices.UPGRADED_INDEX_SUFFIX;
final var projectId = randomProjectIdOrDefault();

// Simulate the post-migration state: the concrete index has the reindexed suffix and an
// outdated managed_index_mappings_version, while the original primary index name and the
// alias are both aliases pointing to the new concrete index.
IndexMetadata.Builder reindexedIndexMeta = IndexMetadata.builder(reindexedIndexName)
.settings(Settings.builder().put(getSettings()).put(IndexMetadata.INDEX_FORMAT_SETTING.getKey(), 6))
.putMapping(Strings.toString(getMappings("1.0.0", 4)))
.putAlias(AliasMetadata.builder(SYSTEM_INDEX_NAME).build())
.putAlias(AliasMetadata.builder(DESCRIPTOR.getAliasName()).build());

final Metadata metadata = Metadata.builder()
.generateClusterUuidIfNeeded()
.put(ProjectMetadata.builder(projectId).put(reindexedIndexMeta))
.build();
final DiscoveryNode node = DiscoveryNodeUtils.builder("1").roles(new HashSet<>(DiscoveryNodeRole.roles())).build();
final DiscoveryNodes nodes = DiscoveryNodes.builder().add(node).masterNodeId(node.getId()).localNodeId(node.getId()).build();
final ClusterState clusterState = ClusterState.builder(CLUSTER_NAME)
.nodes(nodes)
.metadata(metadata)
.routingTable(GlobalRoutingTableTestHelper.buildRoutingTable(metadata, RoutingTable.Builder::addAsNew))
.build();
ProjectState projectState = clusterState.projectState(projectId);

assertThat(
SystemIndexMappingUpdateService.getUpgradeStatus(projectState, DESCRIPTOR),
equalTo(UpgradeStatus.NEEDS_MAPPINGS_UPDATE)
);
}

/**
* Check that this
*/
Expand Down
Loading