diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ILMDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ILMDocumentationIT.java index ae7bd2a09eedb..9f8e09cd1951d 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ILMDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ILMDocumentationIT.java @@ -621,9 +621,6 @@ public void testRetryPolicy() throws Exception { .put("index.lifecycle.name", "my_policy") .build()); client.indices().create(createIndexRequest, RequestOptions.DEFAULT); - assertBusy(() -> assertNotNull(client.indexLifecycle() - .explainLifecycle(new ExplainLifecycleRequest("my_index"), RequestOptions.DEFAULT) - .getIndexResponses().get("my_index").getFailedStep()), 30, TimeUnit.SECONDS); } // tag::ilm-retry-lifecycle-policy-request @@ -644,8 +641,8 @@ public void testRetryPolicy() throws Exception { assertTrue(acknowledged); } catch (ElasticsearchException e) { - // the retry API might fail as the shrink action steps are retryable (so if the retry API reaches ES when ILM is retrying the - // failed `shrink` step, the retry API will fail) + // the retry API might fail as the shrink action steps are retryable (ILM will stuck in the `check-target-shards-count` step + // with no failure, the retry API will fail) // assert that's the exception we encountered (we want to test to fail if there is an actual error with the retry api) assertThat(e.getMessage(), containsStringIgnoringCase("reason=cannot retry an action for an index [my_index] that has not " + "encountered an error when running a Lifecycle Policy")); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/CheckTargetShardsCountStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/CheckTargetShardsCountStep.java new file mode 100644 index 0000000000000..8f463918d739e --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/CheckTargetShardsCountStep.java @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +package org.elasticsearch.xpack.core.ilm; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.index.Index; +import org.elasticsearch.xpack.core.ilm.step.info.SingleMessageFieldInfo; + +import java.util.Locale; + +/** + * This step checks whether the new shrunken index's shards count is a factor of the source index's shards count. + */ +public class CheckTargetShardsCountStep extends ClusterStateWaitStep { + + public static final String NAME = "check-target-shards-count"; + + private final Integer numberOfShards; + + private static final Logger logger = LogManager.getLogger(CheckTargetShardsCountStep.class); + + CheckTargetShardsCountStep(StepKey key, StepKey nextStepKey, Integer numberOfShards) { + super(key, nextStepKey); + this.numberOfShards = numberOfShards; + } + + @Override + public boolean isRetryable() { + return true; + } + + public Integer getNumberOfShards() { + return numberOfShards; + } + + @Override + public Result isConditionMet(Index index, ClusterState clusterState) { + IndexMetadata indexMetadata = clusterState.metadata().index(index); + if (indexMetadata == null) { + // Index must have been since deleted, ignore it + logger.debug("[{}] lifecycle action for index [{}] executed but index no longer exists", + getKey().getAction(), index.getName()); + return new Result(false, null); + } + String indexName = indexMetadata.getIndex().getName(); + if (numberOfShards != null) { + int sourceNumberOfShards = indexMetadata.getNumberOfShards(); + if (sourceNumberOfShards % numberOfShards != 0) { + String policyName = indexMetadata.getSettings().get(LifecycleSettings.LIFECYCLE_NAME); + String errorMessage = String.format(Locale.ROOT, "lifecycle action of policy [%s] for index [%s] cannot make progress " + + "because the target shards count [%d] must be a factor of the source index's shards count [%d]", + policyName, indexName, numberOfShards, sourceNumberOfShards); + logger.debug(errorMessage); + return new Result(false, new SingleMessageFieldInfo(errorMessage)); + } + } + + return new Result(true, null); + } +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ShrinkAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ShrinkAction.java index f251a8c3a07c8..172998b97ab44 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ShrinkAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/ShrinkAction.java @@ -137,6 +137,7 @@ public List toSteps(Client client, String phase, Step.StepKey nextStepKey) StepKey checkNotWriteIndex = new StepKey(phase, NAME, CheckNotDataStreamWriteIndexStep.NAME); StepKey waitForNoFollowerStepKey = new StepKey(phase, NAME, WaitForNoFollowersStep.NAME); StepKey readOnlyKey = new StepKey(phase, NAME, ReadOnlyAction.NAME); + StepKey checkTargetShardsCountKey = new StepKey(phase, NAME, CheckTargetShardsCountStep.NAME); StepKey cleanupShrinkIndexKey = new StepKey(phase, NAME, CleanupShrinkIndexStep.NAME); StepKey generateShrinkIndexNameKey = new StepKey(phase, NAME, GenerateUniqueIndexNameStep.NAME); StepKey setSingleNodeKey = new StepKey(phase, NAME, SetSingleNodeAllocateStep.NAME); @@ -167,7 +168,9 @@ public List toSteps(Client client, String phase, Step.StepKey nextStepKey) CheckNotDataStreamWriteIndexStep checkNotWriteIndexStep = new CheckNotDataStreamWriteIndexStep(checkNotWriteIndex, waitForNoFollowerStepKey); WaitForNoFollowersStep waitForNoFollowersStep = new WaitForNoFollowersStep(waitForNoFollowerStepKey, readOnlyKey, client); - ReadOnlyStep readOnlyStep = new ReadOnlyStep(readOnlyKey, cleanupShrinkIndexKey, client); + ReadOnlyStep readOnlyStep = new ReadOnlyStep(readOnlyKey, checkTargetShardsCountKey, client); + CheckTargetShardsCountStep checkTargetShardsCountStep = new CheckTargetShardsCountStep(checkTargetShardsCountKey, + cleanupShrinkIndexKey, numberOfShards); // we generate a unique shrink index name but we also retry if the allocation of the shrunk index is not possible, so we want to // delete the "previously generated" shrink index (this is a no-op if it's the first run of the action and he haven't generated a // shrink index name) @@ -211,9 +214,9 @@ public List toSteps(Client client, String phase, Step.StepKey nextStepKey) DeleteStep deleteSourceIndexStep = new DeleteStep(deleteIndexKey, isShrunkIndexKey, client); ShrunkenIndexCheckStep waitOnShrinkTakeover = new ShrunkenIndexCheckStep(isShrunkIndexKey, nextStepKey); return Arrays.asList(conditionalSkipShrinkStep, checkNotWriteIndexStep, waitForNoFollowersStep, readOnlyStep, - cleanupShrinkIndexStep, generateUniqueIndexNameStep, setSingleNodeStep, checkShrinkReadyStep, shrink, allocated, - copyMetadata, isDataStreamBranchingStep, aliasSwapAndDelete, waitOnShrinkTakeover, replaceDataStreamBackingIndex, - deleteSourceIndexStep); + checkTargetShardsCountStep, cleanupShrinkIndexStep, generateUniqueIndexNameStep, setSingleNodeStep, checkShrinkReadyStep, + shrink, allocated, copyMetadata, isDataStreamBranchingStep, aliasSwapAndDelete, waitOnShrinkTakeover, + replaceDataStreamBackingIndex, deleteSourceIndexStep); } @Override diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/CheckTargetShardsCountStepTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/CheckTargetShardsCountStepTests.java new file mode 100644 index 0000000000000..9c50e9a20b5cb --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/CheckTargetShardsCountStepTests.java @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +package org.elasticsearch.xpack.core.ilm; + +import org.elasticsearch.Version; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.xpack.core.ilm.Step.StepKey; +import org.elasticsearch.xpack.core.ilm.step.info.SingleMessageFieldInfo; + +import static org.hamcrest.Matchers.is; + +public class CheckTargetShardsCountStepTests extends AbstractStepTestCase { + + @Override + protected CheckTargetShardsCountStep createRandomInstance() { + return new CheckTargetShardsCountStep(randomStepKey(), randomStepKey(), null); + } + + @Override + protected CheckTargetShardsCountStep mutateInstance(CheckTargetShardsCountStep instance) { + StepKey key = instance.getKey(); + StepKey nextKey = instance.getNextStepKey(); + + switch (between(0, 1)) { + case 0: + key = new StepKey(key.getPhase(), key.getAction(), key.getName() + randomAlphaOfLength(5)); + break; + case 1: + nextKey = new StepKey(key.getPhase(), key.getAction(), key.getName() + randomAlphaOfLength(5)); + break; + default: + throw new AssertionError("Illegal randomisation branch"); + } + + return new CheckTargetShardsCountStep(key, nextKey, null); + } + + @Override + protected CheckTargetShardsCountStep copyInstance(CheckTargetShardsCountStep instance) { + return new CheckTargetShardsCountStep(instance.getKey(), instance.getNextStepKey(), instance.getNumberOfShards()); + } + + public void testStepCompleteIfTargetShardsCountIsValid() { + String policyName = "test-ilm-policy"; + IndexMetadata indexMetadata = + IndexMetadata.builder(randomAlphaOfLength(10)).settings(settings(Version.CURRENT) + .put(LifecycleSettings.LIFECYCLE_NAME, policyName)) + .numberOfShards(10).numberOfReplicas(randomIntBetween(0, 5)).build(); + + ClusterState clusterState = ClusterState.builder(emptyClusterState()).metadata( + Metadata.builder().put(indexMetadata, true).build()).build(); + + CheckTargetShardsCountStep checkTargetShardsCountStep = new CheckTargetShardsCountStep(randomStepKey(), randomStepKey(), 2); + + ClusterStateWaitStep.Result result = checkTargetShardsCountStep.isConditionMet(indexMetadata.getIndex(), clusterState); + assertThat(result.isComplete(), is(true)); + } + + public void testStepIncompleteIfTargetShardsCountNotValid() { + String indexName = randomAlphaOfLength(10); + String policyName = "test-ilm-policy"; + IndexMetadata indexMetadata = + IndexMetadata.builder(indexName).settings(settings(Version.CURRENT).put(LifecycleSettings.LIFECYCLE_NAME, policyName)) + .numberOfShards(10).numberOfReplicas(randomIntBetween(0, 5)).build(); + + ClusterState clusterState = ClusterState.builder(emptyClusterState()).metadata( + Metadata.builder().put(indexMetadata, true).build()).build(); + + CheckTargetShardsCountStep checkTargetShardsCountStep = new CheckTargetShardsCountStep(randomStepKey(), randomStepKey(), 3); + + ClusterStateWaitStep.Result result = checkTargetShardsCountStep.isConditionMet(indexMetadata.getIndex(), clusterState); + assertThat(result.isComplete(), is(false)); + SingleMessageFieldInfo info = (SingleMessageFieldInfo) result.getInfomationContext(); + assertThat(info.getMessage(), is("lifecycle action of policy [" + policyName + "] for index [" + indexName + + "] cannot make progress because the target shards count [3] must be a factor of the source index's shards count [10]")); + } +} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ShrinkActionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ShrinkActionTests.java index 855322ba208f1..a6d4d5dde3aa8 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ShrinkActionTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ShrinkActionTests.java @@ -148,23 +148,24 @@ public void testToSteps() { StepKey nextStepKey = new StepKey(randomAlphaOfLengthBetween(1, 10), randomAlphaOfLengthBetween(1, 10), randomAlphaOfLengthBetween(1, 10)); List steps = action.toSteps(null, phase, nextStepKey); - assertThat(steps.size(), equalTo(16)); + assertThat(steps.size(), equalTo(17)); StepKey expectedFirstKey = new StepKey(phase, ShrinkAction.NAME, ShrinkAction.CONDITIONAL_SKIP_SHRINK_STEP); StepKey expectedSecondKey = new StepKey(phase, ShrinkAction.NAME, CheckNotDataStreamWriteIndexStep.NAME); StepKey expectedThirdKey = new StepKey(phase, ShrinkAction.NAME, WaitForNoFollowersStep.NAME); StepKey expectedFourthKey = new StepKey(phase, ShrinkAction.NAME, ReadOnlyAction.NAME); - StepKey expectedFifthKey = new StepKey(phase, ShrinkAction.NAME, CleanupShrinkIndexStep.NAME); - StepKey expectedSixthKey = new StepKey(phase, ShrinkAction.NAME, GenerateUniqueIndexNameStep.NAME); - StepKey expectedSeventhKey = new StepKey(phase, ShrinkAction.NAME, SetSingleNodeAllocateStep.NAME); - StepKey expectedEighthKey = new StepKey(phase, ShrinkAction.NAME, CheckShrinkReadyStep.NAME); - StepKey expectedNinthKey = new StepKey(phase, ShrinkAction.NAME, ShrinkStep.NAME); - StepKey expectedTenthKey = new StepKey(phase, ShrinkAction.NAME, ShrunkShardsAllocatedStep.NAME); - StepKey expectedEleventhKey = new StepKey(phase, ShrinkAction.NAME, CopyExecutionStateStep.NAME); - StepKey expectedTwelveKey = new StepKey(phase, ShrinkAction.NAME, ShrinkAction.CONDITIONAL_DATASTREAM_CHECK_KEY); - StepKey expectedThirteenKey = new StepKey(phase, ShrinkAction.NAME, ShrinkSetAliasStep.NAME); - StepKey expectedFourteenKey = new StepKey(phase, ShrinkAction.NAME, ShrunkenIndexCheckStep.NAME); - StepKey expectedFifteenKey = new StepKey(phase, ShrinkAction.NAME, ReplaceDataStreamBackingIndexStep.NAME); - StepKey expectedSixteenKey = new StepKey(phase, ShrinkAction.NAME, DeleteStep.NAME); + StepKey expectedFifthKey = new StepKey(phase, ShrinkAction.NAME, CheckTargetShardsCountStep.NAME); + StepKey expectedSixthKey = new StepKey(phase, ShrinkAction.NAME, CleanupShrinkIndexStep.NAME); + StepKey expectedSeventhKey = new StepKey(phase, ShrinkAction.NAME, GenerateUniqueIndexNameStep.NAME); + StepKey expectedEighthKey = new StepKey(phase, ShrinkAction.NAME, SetSingleNodeAllocateStep.NAME); + StepKey expectedNinthKey = new StepKey(phase, ShrinkAction.NAME, CheckShrinkReadyStep.NAME); + StepKey expectedTenthKey = new StepKey(phase, ShrinkAction.NAME, ShrinkStep.NAME); + StepKey expectedEleventhKey = new StepKey(phase, ShrinkAction.NAME, ShrunkShardsAllocatedStep.NAME); + StepKey expectedTwelveKey = new StepKey(phase, ShrinkAction.NAME, CopyExecutionStateStep.NAME); + StepKey expectedThirteenKey = new StepKey(phase, ShrinkAction.NAME, ShrinkAction.CONDITIONAL_DATASTREAM_CHECK_KEY); + StepKey expectedFourteenKey = new StepKey(phase, ShrinkAction.NAME, ShrinkSetAliasStep.NAME); + StepKey expectedFifteenKey = new StepKey(phase, ShrinkAction.NAME, ShrunkenIndexCheckStep.NAME); + StepKey expectedSixteenKey = new StepKey(phase, ShrinkAction.NAME, ReplaceDataStreamBackingIndexStep.NAME); + StepKey expectedSeventeenKey = new StepKey(phase, ShrinkAction.NAME, DeleteStep.NAME); assertTrue(steps.get(0) instanceof BranchingStep); assertThat(steps.get(0).getKey(), equalTo(expectedFirstKey)); @@ -184,61 +185,66 @@ public void testToSteps() { assertThat(steps.get(3).getKey(), equalTo(expectedFourthKey)); assertThat(steps.get(3).getNextStepKey(), equalTo(expectedFifthKey)); - assertTrue(steps.get(4) instanceof CleanupShrinkIndexStep); + assertTrue(steps.get(4) instanceof CheckTargetShardsCountStep); assertThat(steps.get(4).getKey(), equalTo(expectedFifthKey)); assertThat(steps.get(4).getNextStepKey(), equalTo(expectedSixthKey)); - assertTrue(steps.get(5) instanceof GenerateUniqueIndexNameStep); + assertTrue(steps.get(5) instanceof CleanupShrinkIndexStep); assertThat(steps.get(5).getKey(), equalTo(expectedSixthKey)); assertThat(steps.get(5).getNextStepKey(), equalTo(expectedSeventhKey)); - assertTrue(steps.get(6) instanceof SetSingleNodeAllocateStep); + assertTrue(steps.get(6) instanceof GenerateUniqueIndexNameStep); assertThat(steps.get(6).getKey(), equalTo(expectedSeventhKey)); assertThat(steps.get(6).getNextStepKey(), equalTo(expectedEighthKey)); - assertTrue(steps.get(7) instanceof ClusterStateWaitUntilThresholdStep); - assertThat(((ClusterStateWaitUntilThresholdStep) steps.get(7)).getStepToExecute(), is(instanceOf(CheckShrinkReadyStep.class))); - // assert in case the threshold is breached we go back to the "cleanup shrunk index" step - assertThat(((ClusterStateWaitUntilThresholdStep) steps.get(7)).getNextKeyOnThreshold(), is(expectedSeventhKey)); + assertTrue(steps.get(7) instanceof SetSingleNodeAllocateStep); assertThat(steps.get(7).getKey(), equalTo(expectedEighthKey)); assertThat(steps.get(7).getNextStepKey(), equalTo(expectedNinthKey)); - assertTrue(steps.get(8) instanceof ShrinkStep); + assertTrue(steps.get(8) instanceof ClusterStateWaitUntilThresholdStep); + assertThat(((ClusterStateWaitUntilThresholdStep) steps.get(8)).getStepToExecute(), is(instanceOf(CheckShrinkReadyStep.class))); + // assert in case the threshold is breached we go back to the "cleanup shrunk index" step + assertThat(((ClusterStateWaitUntilThresholdStep) steps.get(8)).getNextKeyOnThreshold(), is(expectedEighthKey)); assertThat(steps.get(8).getKey(), equalTo(expectedNinthKey)); assertThat(steps.get(8).getNextStepKey(), equalTo(expectedTenthKey)); - assertTrue(steps.get(9) instanceof ClusterStateWaitUntilThresholdStep); + assertTrue(steps.get(9) instanceof ShrinkStep); assertThat(steps.get(9).getKey(), equalTo(expectedTenthKey)); assertThat(steps.get(9).getNextStepKey(), equalTo(expectedEleventhKey)); - assertThat(((ClusterStateWaitUntilThresholdStep) steps.get(9)).getStepToExecute(), is(instanceOf(ShrunkShardsAllocatedStep.class))); - // assert in case the threshold is breached we go back to the "cleanup shrunk index" step - assertThat(((ClusterStateWaitUntilThresholdStep) steps.get(9)).getNextKeyOnThreshold(), is(expectedFifthKey)); - assertTrue(steps.get(10) instanceof CopyExecutionStateStep); + assertTrue(steps.get(10) instanceof ClusterStateWaitUntilThresholdStep); assertThat(steps.get(10).getKey(), equalTo(expectedEleventhKey)); assertThat(steps.get(10).getNextStepKey(), equalTo(expectedTwelveKey)); + assertThat(((ClusterStateWaitUntilThresholdStep) steps.get(10)).getStepToExecute(), + is(instanceOf(ShrunkShardsAllocatedStep.class))); + // assert in case the threshold is breached we go back to the "cleanup shrunk index" step + assertThat(((ClusterStateWaitUntilThresholdStep) steps.get(10)).getNextKeyOnThreshold(), is(expectedSixthKey)); - assertTrue(steps.get(11) instanceof BranchingStep); + assertTrue(steps.get(11) instanceof CopyExecutionStateStep); assertThat(steps.get(11).getKey(), equalTo(expectedTwelveKey)); - expectThrows(IllegalStateException.class, () -> steps.get(11).getNextStepKey()); - assertThat(((BranchingStep) steps.get(11)).getNextStepKeyOnFalse(), equalTo(expectedThirteenKey)); - assertThat(((BranchingStep) steps.get(11)).getNextStepKeyOnTrue(), equalTo(expectedFifteenKey)); + assertThat(steps.get(11).getNextStepKey(), equalTo(expectedThirteenKey)); - assertTrue(steps.get(12) instanceof ShrinkSetAliasStep); + assertTrue(steps.get(12) instanceof BranchingStep); assertThat(steps.get(12).getKey(), equalTo(expectedThirteenKey)); - assertThat(steps.get(12).getNextStepKey(), equalTo(expectedFourteenKey)); + expectThrows(IllegalStateException.class, () -> steps.get(12).getNextStepKey()); + assertThat(((BranchingStep) steps.get(12)).getNextStepKeyOnFalse(), equalTo(expectedFourteenKey)); + assertThat(((BranchingStep) steps.get(12)).getNextStepKeyOnTrue(), equalTo(expectedSixteenKey)); - assertTrue(steps.get(13) instanceof ShrunkenIndexCheckStep); + assertTrue(steps.get(13) instanceof ShrinkSetAliasStep); assertThat(steps.get(13).getKey(), equalTo(expectedFourteenKey)); - assertThat(steps.get(13).getNextStepKey(), equalTo(nextStepKey)); + assertThat(steps.get(13).getNextStepKey(), equalTo(expectedFifteenKey)); - assertTrue(steps.get(14) instanceof ReplaceDataStreamBackingIndexStep); + assertTrue(steps.get(14) instanceof ShrunkenIndexCheckStep); assertThat(steps.get(14).getKey(), equalTo(expectedFifteenKey)); - assertThat(steps.get(14).getNextStepKey(), equalTo(expectedSixteenKey)); + assertThat(steps.get(14).getNextStepKey(), equalTo(nextStepKey)); - assertTrue(steps.get(15) instanceof DeleteStep); + assertTrue(steps.get(15) instanceof ReplaceDataStreamBackingIndexStep); assertThat(steps.get(15).getKey(), equalTo(expectedSixteenKey)); - assertThat(steps.get(15).getNextStepKey(), equalTo(expectedFourteenKey)); + assertThat(steps.get(15).getNextStepKey(), equalTo(expectedSeventeenKey)); + + assertTrue(steps.get(16) instanceof DeleteStep); + assertThat(steps.get(16).getKey(), equalTo(expectedSeventeenKey)); + assertThat(steps.get(16).getNextStepKey(), equalTo(expectedFifteenKey)); } @Override diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ShrinkStepTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ShrinkStepTests.java index 94f28f73373fd..4a8a8ba57ec25 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ShrinkStepTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/ShrinkStepTests.java @@ -6,14 +6,18 @@ */ package org.elasticsearch.xpack.core.ilm; +import org.apache.lucene.util.SetOnce; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.rollover.RolloverResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; import org.elasticsearch.action.support.PlainActionFuture; +import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.AliasMetadata; import org.elasticsearch.cluster.metadata.IndexMetadata; +import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.xpack.core.ilm.Step.StepKey; @@ -22,6 +26,7 @@ import java.util.Collections; import static org.elasticsearch.xpack.core.ilm.LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY; +import static org.elasticsearch.xpack.core.ilm.ShrinkIndexNameSupplier.SHRUNKEN_INDEX_PREFIX; import static org.hamcrest.Matchers.equalTo; public class ShrinkStepTests extends AbstractStepTestCase { @@ -37,7 +42,6 @@ public ShrinkStep createRandomInstance() { } else { maxPrimaryShardSize = new ByteSizeValue(between(1,100)); } - String shrunkIndexPrefix = randomAlphaOfLength(10); return new ShrinkStep(stepKey, nextStepKey, client, numberOfShards, maxPrimaryShardSize); } @@ -124,6 +128,49 @@ public void testPerformAction() throws Exception { Mockito.verify(indicesClient, Mockito.only()).resizeIndex(Mockito.any(), Mockito.any()); } + public void testPerformActionShrunkenIndexExists() throws Exception { + String sourceIndexName = randomAlphaOfLength(10); + String lifecycleName = randomAlphaOfLength(5); + ShrinkStep step = createRandomInstance(); + LifecycleExecutionState.Builder lifecycleState = LifecycleExecutionState.builder(); + lifecycleState.setPhase(step.getKey().getPhase()); + lifecycleState.setAction(step.getKey().getAction()); + lifecycleState.setStep(step.getKey().getName()); + lifecycleState.setIndexCreationDate(randomNonNegativeLong()); + String generatedShrunkenIndexName = GenerateUniqueIndexNameStep.generateValidIndexName(SHRUNKEN_INDEX_PREFIX, sourceIndexName); + lifecycleState.setShrinkIndexName(generatedShrunkenIndexName); + IndexMetadata sourceIndexMetadata = IndexMetadata.builder(sourceIndexName) + .settings(settings(Version.CURRENT) + .put(LifecycleSettings.LIFECYCLE_NAME, lifecycleName) + ) + .putCustom(ILM_CUSTOM_METADATA_KEY, lifecycleState.build().asMap()) + .numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)) + .putAlias(AliasMetadata.builder("my_alias")) + .build(); + + IndexMetadata indexMetadata = IndexMetadata.builder(generatedShrunkenIndexName).settings(settings(Version.CURRENT)) + .numberOfShards(1).numberOfReplicas(0).build(); + ImmutableOpenMap.Builder indices = ImmutableOpenMap.builder().fPut( + generatedShrunkenIndexName, indexMetadata); + ClusterState clusterState = ClusterState.builder(ClusterState.EMPTY_STATE).metadata(Metadata.builder().indices(indices.build())) + .build(); + + final SetOnce conditionMetHolder = new SetOnce<>(); + step.performAction(sourceIndexMetadata, clusterState, null, new ActionListener<>() { + @Override + public void onResponse(Boolean aBoolean) { + conditionMetHolder.set(aBoolean); + } + + @Override + public void onFailure(Exception e) { + fail("onFailure should not be called in this test, called with exception: " + e.getMessage()); + } + }); + + assertTrue(conditionMetHolder.get()); + } + public void testPerformActionIsCompleteForUnAckedRequests() throws Exception { LifecycleExecutionState.Builder lifecycleState = LifecycleExecutionState.builder(); lifecycleState.setIndexCreationDate(randomNonNegativeLong()); diff --git a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ShrinkActionIT.java b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ShrinkActionIT.java index 562c442ba37f0..f48943ec6faa4 100644 --- a/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ShrinkActionIT.java +++ b/x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/ilm/actions/ShrinkActionIT.java @@ -21,6 +21,7 @@ import org.elasticsearch.rest.action.admin.indices.RestPutIndexTemplateAction; import org.elasticsearch.test.rest.ESRestTestCase; import org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider; +import org.elasticsearch.xpack.core.ilm.CheckTargetShardsCountStep; import org.elasticsearch.xpack.core.ilm.LifecycleAction; import org.elasticsearch.xpack.core.ilm.LifecyclePolicy; import org.elasticsearch.xpack.core.ilm.LifecycleSettings; @@ -31,6 +32,7 @@ import org.elasticsearch.xpack.core.ilm.SetSingleNodeAllocateStep; import org.elasticsearch.xpack.core.ilm.ShrinkAction; import org.elasticsearch.xpack.core.ilm.ShrinkStep; +import org.elasticsearch.xpack.core.ilm.Step; import org.junit.Before; import java.io.IOException; @@ -280,10 +282,8 @@ public void testAutomaticRetryFailedShrinkAction() throws Exception { .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)); createNewSingletonPolicy(client(), policy, "warm", new ShrinkAction(numShards + randomIntBetween(1, numShards), null)); updatePolicy(client(), index, policy); - assertBusy(() -> { - String failedStep = getFailedStepForIndex(index); - assertThat(failedStep, equalTo(ShrinkStep.NAME)); - }, 60, TimeUnit.SECONDS); + assertBusy(() -> assertThat(getStepKeyForIndex(client(), index), + equalTo(new Step.StepKey("warm", ShrinkAction.NAME, CheckTargetShardsCountStep.NAME))), 60, TimeUnit.SECONDS); // update policy to be correct createNewSingletonPolicy(client(), policy, "warm", new ShrinkAction(expectedFinalShards, null)); @@ -302,51 +302,4 @@ public void testAutomaticRetryFailedShrinkAction() throws Exception { }); expectThrows(ResponseException.class, () -> indexDocument(client(), index)); } - - public void testShrinkStepMovesForwardIfShrunkIndexIsCreatedBetweenRetries() throws Exception { - int numShards = 4; - int expectedFinalShards = 1; - createIndexWithSettings(client(), index, alias, Settings.builder().put(SETTING_NUMBER_OF_SHARDS, numShards) - .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)); - createNewSingletonPolicy(client(), policy, "warm", new ShrinkAction(numShards + randomIntBetween(1, numShards), null)); - updatePolicy(client(), index, policy); - assertBusy(() -> { - String failedStep = getFailedStepForIndex(index); - assertThat(failedStep, equalTo(ShrinkStep.NAME)); - }, 60, TimeUnit.SECONDS); - - String shrinkIndexName = waitAndGetShrinkIndexName(client(), index); - Request shrinkIndexRequest = new Request("POST", index + "/_shrink/" + shrinkIndexName); - shrinkIndexRequest.setEntity(new StringEntity( - "{\"settings\": {\n" + - " \"" + SETTING_NUMBER_OF_SHARDS + "\": 1,\n" + - " \"" + SETTING_NUMBER_OF_REPLICAS + "\": 0,\n" + - " \"" + LifecycleSettings.LIFECYCLE_NAME + "\": \"" + policy + "\",\n" + - " \"" + IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "_id" + "\": null\n" + - " \n}\n" + - "\n}", ContentType.APPLICATION_JSON)); - client().performRequest(shrinkIndexRequest); - - // assert manually shrunk index is picked up and policy completes successfully - assertBusy(() -> assertTrue(indexExists(shrinkIndexName)), 30, TimeUnit.SECONDS); - assertBusy(() -> assertTrue(aliasExists(shrinkIndexName, index))); - assertBusy(() -> assertThat(getStepKeyForIndex(client(), shrinkIndexName), equalTo(PhaseCompleteStep.finalStep("warm").getKey()))); - assertBusy(() -> { - Map settings = getOnlyIndexSettings(client(), shrinkIndexName); - assertThat(settings.get(SETTING_NUMBER_OF_SHARDS), equalTo(String.valueOf(expectedFinalShards))); - assertThat(settings.get(IndexMetadata.INDEX_BLOCKS_WRITE_SETTING.getKey()), equalTo("true")); - assertThat(settings.get(IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "_id"), nullValue()); - }); - expectThrows(ResponseException.class, () -> indexDocument(client(), index)); - } - - @Nullable - private String getFailedStepForIndex(String indexName) throws IOException { - Map indexResponse = explainIndex(client(), indexName); - if (indexResponse == null) { - return null; - } - - return (String) indexResponse.get("failed_step"); - } }