Skip to content

Commit 053a9a9

Browse files
Fix system index mapping update for reindexed indices after migration (elastic#144782)
SystemIndexMappingUpdateService used projectMetadata.hasIndexAbstraction(d.getPrimaryIndex()) to collect system indices descriptors to check mappings, but then used state.metadata().index(descriptor.getPrimaryIndex()) to get IndexMetadata. This works with indices that were not reindexed, since their name is always the same as the system index's primary name, but after reindexing during migration, the index gains -reindexed-for-<version> suffix and has an alias with the primary name pointing to it. However, index method doesn't support aliases, therefore, IndexMetadata wasn't resolved and mappings were not checked. This change fixes that by introducing use of indices lookup, if there is no metadata for the primary index.
1 parent a0ea369 commit 053a9a9

File tree

4 files changed

+104
-6
lines changed

4 files changed

+104
-6
lines changed

docs/changelog/144782.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
area: Infra/Core
2+
issues:
3+
- 144764
4+
pr: 144782
5+
summary: Fix system index mapping update for reindexed indices after migration
6+
type: bug

server/src/internalClusterTest/java/org/elasticsearch/indices/SystemIndexMappingUpdateServiceIT.java

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
package org.elasticsearch.indices;
1111

12+
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
13+
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
1214
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
1315
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
1416
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
@@ -93,6 +95,40 @@ public void testSystemIndexManagerLeavesNewerMappingsAlone() throws Exception {
9395
assertBusy(() -> assertMappingsAndSettings(TestSystemIndexDescriptor.getNewMappings()));
9496
}
9597

98+
/**
99+
* Reproduces a bug where system index mappings are not updated after a major-version reindex migration.
100+
* <a href="https://github.com/elastic/elasticsearch/issues/144764"></a>
101+
*/
102+
public void testSystemIndexManagerUpgradesMappingsOfReindexedIndex() throws Exception {
103+
internalCluster().startNodes(1);
104+
105+
// Directly set up the post-migration state: a reindexed concrete index with old mappings,
106+
// and the original primary index name as an alias pointing to it.
107+
// Use MIGRATE_SYSTEM_INDEX_CAUSE so that TransportCreateIndexAction allows creating a system
108+
// index whose name is not the descriptor's primary index, mirroring SystemIndexMigrator.
109+
final String reindexedIndexName = PRIMARY_INDEX_NAME + SystemIndices.UPGRADED_INDEX_SUFFIX;
110+
assertAcked(
111+
indicesAdmin().create(
112+
new CreateIndexRequest(reindexedIndexName).cause(SystemIndices.MIGRATE_SYSTEM_INDEX_CAUSE)
113+
.settings(TestSystemIndexDescriptor.SETTINGS)
114+
.mapping(TestSystemIndexDescriptor.getOldMappings())
115+
).actionGet()
116+
);
117+
ensureGreen(reindexedIndexName);
118+
assertAcked(
119+
indicesAdmin().aliases(
120+
new IndicesAliasesRequest(TEST_REQUEST_TIMEOUT, TEST_REQUEST_TIMEOUT).addAliasAction(
121+
IndicesAliasesRequest.AliasActions.add().index(reindexedIndexName).alias(PRIMARY_INDEX_NAME)
122+
).addAliasAction(IndicesAliasesRequest.AliasActions.add().index(reindexedIndexName).alias(INDEX_NAME))
123+
).actionGet()
124+
);
125+
126+
TestSystemIndexDescriptor.useNewMappings.set(true);
127+
triggerClusterStateUpdates();
128+
129+
assertBusy(() -> assertMappingsAndSettings(TestSystemIndexDescriptor.getNewMappings(), reindexedIndexName));
130+
}
131+
96132
/**
97133
* Ensures that we can clear any blocks that get set on managed system indices.
98134
*
@@ -119,29 +155,33 @@ private void triggerClusterStateUpdates() {
119155
indicesAdmin().putTemplate(new PutIndexTemplateRequest(name).patterns(List.of(name))).actionGet();
120156
}
121157

158+
private void assertMappingsAndSettings(String expectedMappings) {
159+
assertMappingsAndSettings(expectedMappings, PRIMARY_INDEX_NAME);
160+
}
161+
122162
/**
123163
* Fetch the mappings and settings for {@link TestSystemIndexDescriptor#INDEX_NAME} and verify that they match the expected values.
124164
*/
125-
private void assertMappingsAndSettings(String expectedMappings) {
165+
private void assertMappingsAndSettings(String expectedMappings, String primaryIndexName) {
126166
final GetMappingsResponse getMappingsResponse = indicesAdmin().getMappings(
127167
new GetMappingsRequest(TEST_REQUEST_TIMEOUT).indices(INDEX_NAME)
128168
).actionGet();
129169

130170
final Map<String, MappingMetadata> mappings = getMappingsResponse.getMappings();
131171
assertThat(
132-
"Expected mappings to contain a key for [" + PRIMARY_INDEX_NAME + "], but found: " + mappings.toString(),
133-
mappings.containsKey(PRIMARY_INDEX_NAME),
172+
"Expected mappings to contain a key for [" + primaryIndexName + "], but found: " + mappings.toString(),
173+
mappings.containsKey(primaryIndexName),
134174
equalTo(true)
135175
);
136-
final Map<String, Object> sourceAsMap = mappings.get(PRIMARY_INDEX_NAME).getSourceAsMap();
176+
final Map<String, Object> sourceAsMap = mappings.get(primaryIndexName).getSourceAsMap();
137177

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

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

144-
final Settings actual = getSettingsResponse.getIndexToSettings().get(PRIMARY_INDEX_NAME);
184+
final Settings actual = getSettingsResponse.getIndexToSettings().get(primaryIndexName);
145185

146186
for (String settingName : TestSystemIndexDescriptor.SETTINGS.keySet()) {
147187
assertThat(actual.get(settingName), equalTo(TestSystemIndexDescriptor.SETTINGS.get(settingName)));

server/src/main/java/org/elasticsearch/indices/SystemIndexMappingUpdateService.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.cluster.ProjectState;
2626
import org.elasticsearch.cluster.health.ClusterHealthStatus;
2727
import org.elasticsearch.cluster.health.ClusterIndexHealth;
28+
import org.elasticsearch.cluster.metadata.IndexAbstraction;
2829
import org.elasticsearch.cluster.metadata.IndexMetadata;
2930
import org.elasticsearch.cluster.metadata.MappingMetadata;
3031
import org.elasticsearch.cluster.metadata.ProjectId;
@@ -249,7 +250,7 @@ public void onFailure(Exception e) {
249250
* @return a summary of the index state, or <code>null</code> if the index doesn't exist
250251
*/
251252
static State calculateIndexState(ProjectState state, SystemIndexDescriptor descriptor) {
252-
final IndexMetadata indexMetadata = state.metadata().index(descriptor.getPrimaryIndex());
253+
IndexMetadata indexMetadata = getSystemIndexMetadata(state, descriptor);
253254

254255
if (indexMetadata == null) {
255256
return null;
@@ -278,6 +279,21 @@ static State calculateIndexState(ProjectState state, SystemIndexDescriptor descr
278279
return new State(indexState, indexHealth, isIndexUpToDate, isMappingIsUpToDate);
279280
}
280281

282+
private static IndexMetadata getSystemIndexMetadata(ProjectState state, SystemIndexDescriptor descriptor) {
283+
String primaryIndexName = descriptor.getPrimaryIndex();
284+
ProjectMetadata projectMetadata = state.metadata();
285+
IndexMetadata indexMetadata = projectMetadata.index(primaryIndexName);
286+
if (indexMetadata == null) {
287+
// The primary index name might be an alias pointing to the concrete index
288+
// (e.g. ".fleet-agents-7" → ".fleet-agents-7-reindexed-for-9").
289+
IndexAbstraction indexAbstraction = projectMetadata.getIndicesLookup().get(primaryIndexName);
290+
if (indexAbstraction != null && indexAbstraction.getWriteIndex() != null) {
291+
indexMetadata = projectMetadata.getIndexSafe(indexAbstraction.getWriteIndex());
292+
}
293+
}
294+
return indexMetadata;
295+
}
296+
281297
/**
282298
* Checks whether an index's mappings are up-to-date. If an index is encountered that has
283299
* a version higher than Version.CURRENT, it is still considered up-to-date.

server/src/test/java/org/elasticsearch/indices/SystemIndexMappingUpdateServiceTests.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,42 @@ public void testManagerSubmitsPutRequest() {
286286
verify(client, times(1)).execute(same(TransportPutMappingAction.TYPE), any(PutMappingRequest.class), any());
287287
}
288288

289+
/**
290+
* Reproduces a bug where SystemIndexMappingUpdateService incorrectly reports UP_TO_DATE for an index whose mappings are
291+
* outdated after a major-version migration (reindex).
292+
*/
293+
public void testManagerDetectsOutdatedMappingsInReindexedIndex() {
294+
final String reindexedIndexName = SYSTEM_INDEX_NAME + SystemIndices.UPGRADED_INDEX_SUFFIX;
295+
final var projectId = randomProjectIdOrDefault();
296+
297+
// Simulate the post-migration state: the concrete index has the reindexed suffix and an
298+
// outdated managed_index_mappings_version, while the original primary index name and the
299+
// alias are both aliases pointing to the new concrete index.
300+
IndexMetadata.Builder reindexedIndexMeta = IndexMetadata.builder(reindexedIndexName)
301+
.settings(Settings.builder().put(getSettings()).put(IndexMetadata.INDEX_FORMAT_SETTING.getKey(), 6))
302+
.putMapping(Strings.toString(getMappings("1.0.0", 4)))
303+
.putAlias(AliasMetadata.builder(SYSTEM_INDEX_NAME).build())
304+
.putAlias(AliasMetadata.builder(DESCRIPTOR.getAliasName()).build());
305+
306+
final Metadata metadata = Metadata.builder()
307+
.generateClusterUuidIfNeeded()
308+
.put(ProjectMetadata.builder(projectId).put(reindexedIndexMeta))
309+
.build();
310+
final DiscoveryNode node = DiscoveryNodeUtils.builder("1").roles(new HashSet<>(DiscoveryNodeRole.roles())).build();
311+
final DiscoveryNodes nodes = DiscoveryNodes.builder().add(node).masterNodeId(node.getId()).localNodeId(node.getId()).build();
312+
final ClusterState clusterState = ClusterState.builder(CLUSTER_NAME)
313+
.nodes(nodes)
314+
.metadata(metadata)
315+
.routingTable(GlobalRoutingTableTestHelper.buildRoutingTable(metadata, RoutingTable.Builder::addAsNew))
316+
.build();
317+
ProjectState projectState = clusterState.projectState(projectId);
318+
319+
assertThat(
320+
SystemIndexMappingUpdateService.getUpgradeStatus(projectState, DESCRIPTOR),
321+
equalTo(UpgradeStatus.NEEDS_MAPPINGS_UPDATE)
322+
);
323+
}
324+
289325
/**
290326
* Check that this
291327
*/

0 commit comments

Comments
 (0)