Skip to content

Commit 3bbf8fe

Browse files
[8.19] Fix system index mapping update for reindexed indices after migration (#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. (cherry picked from commit 29d7e2a)
1 parent 671f01b commit 3bbf8fe

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,27 +155,31 @@ 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(new GetMappingsRequest().indices(INDEX_NAME))
127167
.actionGet();
128168

129169
final Map<String, MappingMetadata> mappings = getMappingsResponse.getMappings();
130170
assertThat(
131-
"Expected mappings to contain a key for [" + PRIMARY_INDEX_NAME + "], but found: " + mappings.toString(),
132-
mappings.containsKey(PRIMARY_INDEX_NAME),
171+
"Expected mappings to contain a key for [" + primaryIndexName + "], but found: " + mappings.toString(),
172+
mappings.containsKey(primaryIndexName),
133173
equalTo(true)
134174
);
135-
final Map<String, Object> sourceAsMap = mappings.get(PRIMARY_INDEX_NAME).getSourceAsMap();
175+
final Map<String, Object> sourceAsMap = mappings.get(primaryIndexName).getSourceAsMap();
136176

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

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

142-
final Settings actual = getSettingsResponse.getIndexToSettings().get(PRIMARY_INDEX_NAME);
182+
final Settings actual = getSettingsResponse.getIndexToSettings().get(primaryIndexName);
143183

144184
for (String settingName : TestSystemIndexDescriptor.SETTINGS.keySet()) {
145185
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
@@ -24,6 +24,7 @@
2424
import org.elasticsearch.cluster.ClusterStateListener;
2525
import org.elasticsearch.cluster.health.ClusterHealthStatus;
2626
import org.elasticsearch.cluster.health.ClusterIndexHealth;
27+
import org.elasticsearch.cluster.metadata.IndexAbstraction;
2728
import org.elasticsearch.cluster.metadata.IndexMetadata;
2829
import org.elasticsearch.cluster.metadata.MappingMetadata;
2930
import org.elasticsearch.cluster.metadata.Metadata;
@@ -228,7 +229,7 @@ public void onFailure(Exception e) {
228229
* @return a summary of the index state, or <code>null</code> if the index doesn't exist
229230
*/
230231
static State calculateIndexState(ClusterState state, SystemIndexDescriptor descriptor) {
231-
final IndexMetadata indexMetadata = state.metadata().index(descriptor.getPrimaryIndex());
232+
IndexMetadata indexMetadata = getSystemIndexMetadata(state, descriptor);
232233

233234
if (indexMetadata == null) {
234235
return null;
@@ -257,6 +258,21 @@ static State calculateIndexState(ClusterState state, SystemIndexDescriptor descr
257258
return new State(indexState, indexHealth, isIndexUpToDate, isMappingIsUpToDate);
258259
}
259260

261+
private static IndexMetadata getSystemIndexMetadata(ProjectState state, SystemIndexDescriptor descriptor) {
262+
String primaryIndexName = descriptor.getPrimaryIndex();
263+
ProjectMetadata projectMetadata = state.metadata();
264+
IndexMetadata indexMetadata = projectMetadata.index(primaryIndexName);
265+
if (indexMetadata == null) {
266+
// The primary index name might be an alias pointing to the concrete index
267+
// (e.g. ".fleet-agents-7" → ".fleet-agents-7-reindexed-for-9").
268+
IndexAbstraction indexAbstraction = projectMetadata.getIndicesLookup().get(primaryIndexName);
269+
if (indexAbstraction != null && indexAbstraction.getWriteIndex() != null) {
270+
indexMetadata = projectMetadata.getIndexSafe(indexAbstraction.getWriteIndex());
271+
}
272+
}
273+
return indexMetadata;
274+
}
275+
260276
/**
261277
* Checks whether an index's mappings are up-to-date. If an index is encountered that has
262278
* 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
@@ -273,6 +273,42 @@ public void testManagerSubmitsPutRequest() {
273273
verify(client, times(1)).execute(same(TransportPutMappingAction.TYPE), any(PutMappingRequest.class), any());
274274
}
275275

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

0 commit comments

Comments
 (0)