Skip to content

Commit 98e80e6

Browse files
authored
Fix Rollover error when alias has closed indices (#47839)
Rollover previously requested index stats for all indices in the provided alias, which causes an exception when there is a closed index with that alias. This commit adjusts the IndicesOptions used on the index stats request so that closed indices are ignored, rather than throwing an exception. This is mostly a backport of #47148, but the behavior is slightly different: If the write index is closed, rollover will throw an exception, as 6.8 cannot retrieve index stats for closed indices.
1 parent 3b43466 commit 98e80e6

File tree

3 files changed

+91
-21
lines changed

3 files changed

+91
-21
lines changed

server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesClusterStateUpdateRequest;
2424
import org.elasticsearch.action.admin.indices.create.CreateIndexClusterStateUpdateRequest;
2525
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
26+
import org.elasticsearch.action.admin.indices.stats.IndicesStatsAction;
27+
import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest;
2628
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
2729
import org.elasticsearch.action.support.ActionFilters;
2830
import org.elasticsearch.action.support.ActiveShardCount;
@@ -49,6 +51,8 @@
4951
import org.elasticsearch.common.settings.Settings;
5052
import org.elasticsearch.common.unit.ByteSizeValue;
5153
import org.elasticsearch.index.shard.DocsStats;
54+
import org.elasticsearch.indices.IndexClosedException;
55+
import org.elasticsearch.tasks.Task;
5256
import org.elasticsearch.threadpool.ThreadPool;
5357
import org.elasticsearch.transport.TransportService;
5458

@@ -107,12 +111,21 @@ protected ClusterBlockException checkBlock(RolloverRequest request, ClusterState
107111
}
108112

109113
@Override
110-
protected void masterOperation(final RolloverRequest rolloverRequest, final ClusterState state,
114+
protected void masterOperation(RolloverRequest request, ClusterState state,
115+
ActionListener<RolloverResponse> listener) throws Exception {
116+
throw new UnsupportedOperationException("The task parameter is required");
117+
}
118+
119+
@Override
120+
protected void masterOperation(Task task, final RolloverRequest rolloverRequest, final ClusterState state,
111121
final ActionListener<RolloverResponse> listener) {
112122
final MetaData metaData = state.metaData();
113123
validate(metaData, rolloverRequest);
114124
final AliasOrIndex.Alias alias = (AliasOrIndex.Alias) metaData.getAliasAndIndexLookup().get(rolloverRequest.getAlias());
115125
final IndexMetaData indexMetaData = alias.getWriteIndex();
126+
if (IndexMetaData.State.CLOSE.equals(indexMetaData.getState())) {
127+
throw new IndexClosedException(indexMetaData.getIndex());
128+
}
116129
final boolean explicitWriteIndex = Boolean.TRUE.equals(indexMetaData.getAliases().get(alias.getAliasName()).writeIndex());
117130
final String sourceProvidedName = indexMetaData.getSettings().get(IndexMetaData.SETTING_INDEX_PROVIDED_NAME,
118131
indexMetaData.getIndex().getName());
@@ -123,7 +136,12 @@ protected void masterOperation(final RolloverRequest rolloverRequest, final Clus
123136
final String rolloverIndexName = indexNameExpressionResolver.resolveDateMathExpression(unresolvedName);
124137
MetaDataCreateIndexService.validateIndexName(rolloverIndexName, state); // will fail if the index already exists
125138
checkNoDuplicatedAliasInIndexTemplate(metaData, rolloverIndexName, rolloverRequest.getAlias());
126-
client.admin().indices().prepareStats(rolloverRequest.getAlias()).clear().setDocs(true).execute(
139+
IndicesStatsRequest statsRequest = new IndicesStatsRequest().indices(rolloverRequest.getAlias())
140+
.clear()
141+
.indicesOptions(IndicesOptions.fromOptions(true, false, true, true))
142+
.docs(true);
143+
statsRequest.setParentTask(clusterService.localNode().getId(), task.getId());
144+
client.execute(IndicesStatsAction.INSTANCE, statsRequest,
127145
new ActionListener<IndicesStatsResponse>() {
128146
@Override
129147
public void onResponse(IndicesStatsResponse statsResponse) {

server/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.elasticsearch.common.unit.ByteSizeUnit;
3030
import org.elasticsearch.common.unit.ByteSizeValue;
3131
import org.elasticsearch.common.unit.TimeValue;
32+
import org.elasticsearch.indices.IndexClosedException;
3233
import org.elasticsearch.plugins.Plugin;
3334
import org.elasticsearch.test.ESIntegTestCase;
3435
import org.elasticsearch.test.InternalSettingsPlugin;
@@ -40,6 +41,7 @@
4041
import java.util.List;
4142
import java.util.Set;
4243

44+
import static org.elasticsearch.index.mapper.MapperService.SINGLE_MAPPING_NAME;
4345
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
4446
import static org.hamcrest.Matchers.containsInAnyOrder;
4547
import static org.hamcrest.Matchers.equalTo;
@@ -381,4 +383,56 @@ public void testRejectIfAliasFoundInTemplate() throws Exception {
381383
assertThat(error.getMessage(), equalTo(
382384
"Rollover alias [logs-write] can point to multiple indices, found duplicated alias [[logs-write]] in index template [logs]"));
383385
}
386+
387+
public void testRolloverWithClosedIndexInAlias() throws Exception {
388+
final String aliasName = "alias";
389+
final String openNonwriteIndex = "open-index-nonwrite";
390+
final String closedIndex = "closed-index-nonwrite";
391+
final String writeIndexPrefix = "write-index-";
392+
assertAcked(prepareCreate(openNonwriteIndex).addAlias(new Alias(aliasName)).get());
393+
assertAcked(prepareCreate(closedIndex).addAlias(new Alias(aliasName)).get());
394+
assertAcked(prepareCreate(writeIndexPrefix + "000001").addAlias(new Alias(aliasName).writeIndex(true)).get());
395+
396+
index(closedIndex, SINGLE_MAPPING_NAME, null, "{\"foo\": \"bar\"}");
397+
index(aliasName, SINGLE_MAPPING_NAME, null, "{\"foo\": \"bar\"}");
398+
index(aliasName, SINGLE_MAPPING_NAME, null, "{\"foo\": \"bar\"}");
399+
refresh(aliasName);
400+
401+
assertAcked(client().admin().indices().prepareClose(closedIndex).get());
402+
403+
RolloverResponse rolloverResponse = client().admin().indices().prepareRolloverIndex(aliasName)
404+
.addMaxIndexDocsCondition(1)
405+
.get();
406+
assertTrue(rolloverResponse.isRolledOver());
407+
assertEquals(writeIndexPrefix + "000001", rolloverResponse.getOldIndex());
408+
assertEquals(writeIndexPrefix + "000002", rolloverResponse.getNewIndex());
409+
}
410+
411+
public void testRolloverWithClosedWriteIndex() throws Exception {
412+
final String aliasName = "alias";
413+
final String openNonwriteIndex = "open-index-nonwrite";
414+
final String closedIndex = "closed-index-nonwrite";
415+
final String writeIndexPrefix = "write-index-";
416+
assertAcked(prepareCreate(openNonwriteIndex).addAlias(new Alias(aliasName)).get());
417+
assertAcked(prepareCreate(closedIndex).addAlias(new Alias(aliasName)).get());
418+
assertAcked(prepareCreate(writeIndexPrefix + "000001").addAlias(new Alias(aliasName).writeIndex(true)).get());
419+
420+
index(closedIndex, SINGLE_MAPPING_NAME, null, "{\"foo\": \"bar\"}");
421+
index(aliasName, SINGLE_MAPPING_NAME, null, "{\"foo\": \"bar\"}");
422+
index(aliasName, SINGLE_MAPPING_NAME, null, "{\"foo\": \"bar\"}");
423+
refresh(aliasName);
424+
425+
assertAcked(client().admin().indices().prepareClose(closedIndex).get());
426+
assertAcked(client().admin().indices().prepareClose(writeIndexPrefix + "000001").get());
427+
ensureGreen(aliasName);
428+
429+
try {
430+
RolloverResponse rolloverResponse = client().admin().indices().prepareRolloverIndex(aliasName)
431+
.addMaxIndexDocsCondition(1)
432+
.get();
433+
fail("rolling over an alias with a closed write index should have failed");
434+
} catch (IndexClosedException ex) {
435+
// this is what we expect
436+
}
437+
}
384438
}

server/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@
2525
import org.elasticsearch.action.admin.indices.create.CreateIndexClusterStateUpdateRequest;
2626
import org.elasticsearch.action.admin.indices.stats.CommonStats;
2727
import org.elasticsearch.action.admin.indices.stats.IndexStats;
28-
import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequestBuilder;
28+
import org.elasticsearch.action.admin.indices.stats.IndicesStatsAction;
29+
import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest;
2930
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
3031
import org.elasticsearch.action.support.ActionFilters;
3132
import org.elasticsearch.action.support.ActiveShardCount;
3233
import org.elasticsearch.action.support.PlainActionFuture;
33-
import org.elasticsearch.client.AdminClient;
3434
import org.elasticsearch.client.Client;
35-
import org.elasticsearch.client.IndicesAdminClient;
3635
import org.elasticsearch.cluster.ClusterName;
3736
import org.elasticsearch.cluster.ClusterState;
3837
import org.elasticsearch.cluster.metadata.AliasAction;
@@ -43,6 +42,7 @@
4342
import org.elasticsearch.cluster.metadata.MetaData;
4443
import org.elasticsearch.cluster.metadata.MetaDataCreateIndexService;
4544
import org.elasticsearch.cluster.metadata.MetaDataIndexAliasesService;
45+
import org.elasticsearch.cluster.node.DiscoveryNode;
4646
import org.elasticsearch.cluster.service.ClusterService;
4747
import org.elasticsearch.common.UUIDs;
4848
import org.elasticsearch.common.settings.Settings;
@@ -53,6 +53,7 @@
5353
import org.elasticsearch.index.Index;
5454
import org.elasticsearch.index.shard.DocsStats;
5555
import org.elasticsearch.rest.action.cat.RestIndicesActionTests;
56+
import org.elasticsearch.tasks.Task;
5657
import org.elasticsearch.test.ESTestCase;
5758
import org.elasticsearch.threadpool.ThreadPool;
5859
import org.elasticsearch.transport.TransportService;
@@ -345,9 +346,12 @@ public void testRejectDuplicateAlias() {
345346
assertThat(ex.getMessage(), containsString("index template [test-template]"));
346347
}
347348

348-
public void testConditionEvaluationWhenAliasToWriteAndReadIndicesConsidersOnlyPrimariesFromWriteIndex() {
349+
public void testConditionEvaluationWhenAliasToWriteAndReadIndicesConsidersOnlyPrimariesFromWriteIndex() throws Exception {
349350
final TransportService mockTransportService = mock(TransportService.class);
350351
final ClusterService mockClusterService = mock(ClusterService.class);
352+
final DiscoveryNode mockNode = mock(DiscoveryNode.class);
353+
when(mockNode.getId()).thenReturn("mocknode");
354+
when(mockClusterService.localNode()).thenReturn(mockNode);
351355
final ThreadPool mockThreadPool = mock(ThreadPool.class);
352356
final MetaDataCreateIndexService mockCreateIndexService = mock(MetaDataCreateIndexService.class);
353357
final IndexNameExpressionResolver mockIndexNameExpressionResolver = mock(IndexNameExpressionResolver.class);
@@ -356,31 +360,25 @@ public void testConditionEvaluationWhenAliasToWriteAndReadIndicesConsidersOnlyPr
356360
final MetaDataIndexAliasesService mdIndexAliasesService = mock(MetaDataIndexAliasesService.class);
357361

358362
final Client mockClient = mock(Client.class);
359-
final AdminClient mockAdminClient = mock(AdminClient.class);
360-
final IndicesAdminClient mockIndicesAdminClient = mock(IndicesAdminClient.class);
361-
when(mockClient.admin()).thenReturn(mockAdminClient);
362-
when(mockAdminClient.indices()).thenReturn(mockIndicesAdminClient);
363363

364-
final IndicesStatsRequestBuilder mockIndicesStatsBuilder = mock(IndicesStatsRequestBuilder.class);
365-
when(mockIndicesAdminClient.prepareStats(any())).thenReturn(mockIndicesStatsBuilder);
366364
final Map<String, IndexStats> indexStats = new HashMap<>();
367365
int total = randomIntBetween(500, 1000);
368366
indexStats.put("logs-index-000001", createIndexStats(200L, total));
369367
indexStats.put("logs-index-000002", createIndexStats(300L, total));
370368
final IndicesStatsResponse statsResponse = createAliasToMultipleIndicesStatsResponse(indexStats);
371-
when(mockIndicesStatsBuilder.clear()).thenReturn(mockIndicesStatsBuilder);
372-
when(mockIndicesStatsBuilder.setDocs(true)).thenReturn(mockIndicesStatsBuilder);
373369

374-
assert statsResponse.getPrimaries().getDocs().getCount() == 500L;
375-
assert statsResponse.getTotal().getDocs().getCount() == (total + total);
376370

377371
doAnswer(invocation -> {
378372
Object[] args = invocation.getArguments();
379-
assert args.length == 1;
380-
ActionListener<IndicesStatsResponse> listener = (ActionListener<IndicesStatsResponse>) args[0];
373+
assert args.length == 3;
374+
@SuppressWarnings("unchecked")
375+
ActionListener<IndicesStatsResponse> listener = (ActionListener<IndicesStatsResponse>) args[2];
381376
listener.onResponse(statsResponse);
382377
return null;
383-
}).when(mockIndicesStatsBuilder).execute(any(ActionListener.class));
378+
}).when(mockClient).execute(any(IndicesStatsAction.class), any(IndicesStatsRequest.class), any(ActionListener.class));
379+
380+
assert statsResponse.getPrimaries().getDocs().getCount() == 500L;
381+
assert statsResponse.getTotal().getDocs().getCount() == (total + total);
384382

385383
final IndexMetaData.Builder indexMetaData = IndexMetaData.builder("logs-index-000001")
386384
.putAlias(AliasMetaData.builder("logs-alias").writeIndex(false).build()).settings(settings(Version.CURRENT))
@@ -401,7 +399,7 @@ public void testConditionEvaluationWhenAliasToWriteAndReadIndicesConsidersOnlyPr
401399
RolloverRequest rolloverRequest = new RolloverRequest("logs-alias", "logs-index-000003");
402400
rolloverRequest.addMaxIndexDocsCondition(500L);
403401
rolloverRequest.dryRun(true);
404-
transportRolloverAction.masterOperation(rolloverRequest, stateBefore, future);
402+
transportRolloverAction.masterOperation(mock(Task.class), rolloverRequest, stateBefore, future);
405403

406404
RolloverResponse response = future.actionGet();
407405
assertThat(response.getOldIndex(), equalTo("logs-index-000002"));
@@ -417,7 +415,7 @@ public void testConditionEvaluationWhenAliasToWriteAndReadIndicesConsidersOnlyPr
417415
rolloverRequest = new RolloverRequest("logs-alias", "logs-index-000003");
418416
rolloverRequest.addMaxIndexDocsCondition(300L);
419417
rolloverRequest.dryRun(true);
420-
transportRolloverAction.masterOperation(rolloverRequest, stateBefore, future);
418+
transportRolloverAction.masterOperation(mock(Task.class), rolloverRequest, stateBefore, future);
421419

422420
response = future.actionGet();
423421
assertThat(response.getOldIndex(), equalTo("logs-index-000002"));

0 commit comments

Comments
 (0)