Skip to content

Commit 37b0dc7

Browse files
committed
Skip Shrink when numberOfShards not changed
Previously, ShrinkAction would fail if it was executed on an index that had the same number of shards as the target shrunken number. This PR introduced a new BranchingStep that is used inside of ShrinkAction to branch which step to move to next, depending on the shard values. So no shrink will occur if the shard count is unchanged.
1 parent 6ed35fb commit 37b0dc7

File tree

8 files changed

+336
-34
lines changed

8 files changed

+336
-34
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.core.indexlifecycle;
8+
9+
import org.apache.logging.log4j.LogManager;
10+
import org.apache.logging.log4j.Logger;
11+
import org.apache.lucene.util.SetOnce;
12+
import org.elasticsearch.cluster.ClusterState;
13+
import org.elasticsearch.cluster.metadata.IndexMetaData;
14+
import org.elasticsearch.index.Index;
15+
16+
import java.util.Objects;
17+
import java.util.function.BiPredicate;
18+
19+
public class BranchingStep extends ClusterStateActionStep {
20+
public static final String NAME = "branch";
21+
22+
private static final Logger logger = LogManager.getLogger(BranchingStep.class);
23+
24+
private StepKey nextStepKeyOnFalse;
25+
private StepKey nextStepKeyOnTrue;
26+
private BiPredicate<Index, ClusterState> predicate;
27+
private SetOnce<Boolean> predicateValue;
28+
29+
/**
30+
* {@link BranchingStep} is a step whose next step is based on
31+
* the return value of a specific predicate.
32+
*
33+
* @param key the step's key
34+
* @param nextStepKeyOnFalse the key of the step to run if predicate returns false
35+
* @param nextStepKeyOnTrue the key of the step to run if predicate returns true
36+
* @param predicate the condition to check when deciding which step to run next
37+
*/
38+
public BranchingStep(StepKey key, StepKey nextStepKeyOnFalse, StepKey nextStepKeyOnTrue, BiPredicate<Index, ClusterState> predicate) {
39+
// super.nextStepKey is set to null since it is not used by this step
40+
super(key, null);
41+
this.nextStepKeyOnFalse = nextStepKeyOnFalse;
42+
this.nextStepKeyOnTrue = nextStepKeyOnTrue;
43+
this.predicate = predicate;
44+
this.predicateValue = new SetOnce<>();
45+
}
46+
47+
@Override
48+
public ClusterState performAction(Index index, ClusterState clusterState) {
49+
IndexMetaData indexMetaData = clusterState.metaData().index(index);
50+
if (indexMetaData == null) {
51+
// Index must have been since deleted, ignore it
52+
logger.debug("[{}] lifecycle action for index [{}] executed but index no longer exists", getKey().getAction(), index.getName());
53+
return clusterState;
54+
}
55+
predicateValue.set(predicate.test(index, clusterState));
56+
return clusterState;
57+
}
58+
59+
/**
60+
* This method returns the next step to execute based on the predicate. If
61+
* the predicate returned true, then nextStepKeyOnTrue is the key of the
62+
* next step to run, otherwise nextStepKeyOnFalse is.
63+
*
64+
* throws {@link UnsupportedOperationException} if performAction was not called yet
65+
*
66+
* @return next step to execute
67+
*/
68+
@Override
69+
public final StepKey getNextStepKey() {
70+
if (predicateValue.get() == null) {
71+
throw new UnsupportedOperationException("Cannot call getNextStepKey before performAction");
72+
}
73+
return predicateValue.get() ? nextStepKeyOnTrue : nextStepKeyOnFalse;
74+
}
75+
76+
/**
77+
* @return the next step if {@code predicate} is false
78+
*/
79+
final StepKey getNextStepKeyOnFalse() {
80+
return nextStepKeyOnFalse;
81+
}
82+
83+
/**
84+
* @return the next step if {@code predicate} is true
85+
*/
86+
final StepKey getNextStepKeyOnTrue() {
87+
return nextStepKeyOnTrue;
88+
}
89+
90+
public final BiPredicate<Index, ClusterState> getPredicate() {
91+
return predicate;
92+
}
93+
94+
@Override
95+
public boolean equals(Object o) {
96+
if (this == o) return true;
97+
if (o == null || getClass() != o.getClass()) return false;
98+
if (!super.equals(o)) return false;
99+
BranchingStep that = (BranchingStep) o;
100+
return super.equals(o)
101+
&& Objects.equals(nextStepKeyOnFalse, that.nextStepKeyOnFalse)
102+
&& Objects.equals(nextStepKeyOnTrue, that.nextStepKeyOnTrue);
103+
}
104+
105+
@Override
106+
public int hashCode() {
107+
return Objects.hash(super.hashCode(), nextStepKeyOnFalse, nextStepKeyOnTrue);
108+
}
109+
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/ShrinkAction.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public boolean isSafeAction() {
8585
public List<Step> toSteps(Client client, String phase, Step.StepKey nextStepKey) {
8686
Settings readOnlySettings = Settings.builder().put(IndexMetaData.SETTING_BLOCKS_WRITE, true).build();
8787

88+
StepKey branchingKey = new StepKey(phase, NAME, BranchingStep.NAME);
8889
StepKey readOnlyKey = new StepKey(phase, NAME, ReadOnlyAction.NAME);
8990
StepKey setSingleNodeKey = new StepKey(phase, NAME, SetSingleNodeAllocateStep.NAME);
9091
StepKey allocationRoutedKey = new StepKey(phase, NAME, CheckShrinkReadyStep.NAME);
@@ -94,6 +95,8 @@ public List<Step> toSteps(Client client, String phase, Step.StepKey nextStepKey)
9495
StepKey aliasKey = new StepKey(phase, NAME, ShrinkSetAliasStep.NAME);
9596
StepKey isShrunkIndexKey = new StepKey(phase, NAME, ShrunkenIndexCheckStep.NAME);
9697

98+
BranchingStep conditionalSkipShrinkStep = new BranchingStep(branchingKey, readOnlyKey, nextStepKey,
99+
(index, clusterState) -> clusterState.getMetaData().index(index).getNumberOfShards() == numberOfShards);
97100
UpdateSettingsStep readOnlyStep = new UpdateSettingsStep(readOnlyKey, setSingleNodeKey, client, readOnlySettings);
98101
SetSingleNodeAllocateStep setSingleNodeStep = new SetSingleNodeAllocateStep(setSingleNodeKey, allocationRoutedKey, client);
99102
CheckShrinkReadyStep checkShrinkReadyStep = new CheckShrinkReadyStep(allocationRoutedKey, shrinkKey);
@@ -102,12 +105,13 @@ public List<Step> toSteps(Client client, String phase, Step.StepKey nextStepKey)
102105
CopyExecutionStateStep copyMetadata = new CopyExecutionStateStep(copyMetadataKey, aliasKey, SHRUNKEN_INDEX_PREFIX);
103106
ShrinkSetAliasStep aliasSwapAndDelete = new ShrinkSetAliasStep(aliasKey, isShrunkIndexKey, client, SHRUNKEN_INDEX_PREFIX);
104107
ShrunkenIndexCheckStep waitOnShrinkTakeover = new ShrunkenIndexCheckStep(isShrunkIndexKey, nextStepKey, SHRUNKEN_INDEX_PREFIX);
105-
return Arrays.asList(readOnlyStep, setSingleNodeStep, checkShrinkReadyStep, shrink, allocated, copyMetadata,
106-
aliasSwapAndDelete, waitOnShrinkTakeover);
108+
return Arrays.asList(conditionalSkipShrinkStep, readOnlyStep, setSingleNodeStep, checkShrinkReadyStep, shrink, allocated,
109+
copyMetadata, aliasSwapAndDelete, waitOnShrinkTakeover);
107110
}
108111

109112
@Override
110113
public List<StepKey> toStepKeys(String phase) {
114+
StepKey conditionalSkipKey = new StepKey(phase, NAME, BranchingStep.NAME);
111115
StepKey readOnlyKey = new StepKey(phase, NAME, ReadOnlyAction.NAME);
112116
StepKey setSingleNodeKey = new StepKey(phase, NAME, SetSingleNodeAllocateStep.NAME);
113117
StepKey checkShrinkReadyKey = new StepKey(phase, NAME, CheckShrinkReadyStep.NAME);
@@ -116,7 +120,7 @@ public List<StepKey> toStepKeys(String phase) {
116120
StepKey copyMetadataKey = new StepKey(phase, NAME, CopyExecutionStateStep.NAME);
117121
StepKey aliasKey = new StepKey(phase, NAME, ShrinkSetAliasStep.NAME);
118122
StepKey isShrunkIndexKey = new StepKey(phase, NAME, ShrunkenIndexCheckStep.NAME);
119-
return Arrays.asList(readOnlyKey, setSingleNodeKey, checkShrinkReadyKey, shrinkKey, enoughShardsKey,
123+
return Arrays.asList(conditionalSkipKey, readOnlyKey, setSingleNodeKey, checkShrinkReadyKey, shrinkKey, enoughShardsKey,
120124
copyMetadataKey, aliasKey, isShrunkIndexKey);
121125
}
122126

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/Step.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public final StepKey getKey() {
3434
return key;
3535
}
3636

37-
public final StepKey getNextStepKey() {
37+
public StepKey getNextStepKey() {
3838
return nextStepKey;
3939
}
4040

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.core.indexlifecycle;
7+
8+
import org.apache.lucene.util.SetOnce;
9+
import org.elasticsearch.Version;
10+
import org.elasticsearch.cluster.ClusterName;
11+
import org.elasticsearch.cluster.ClusterState;
12+
import org.elasticsearch.cluster.metadata.IndexMetaData;
13+
import org.elasticsearch.cluster.metadata.MetaData;
14+
import org.elasticsearch.index.Index;
15+
import org.elasticsearch.xpack.core.indexlifecycle.Step.StepKey;
16+
17+
import java.util.function.BiPredicate;
18+
19+
import static org.hamcrest.Matchers.equalTo;
20+
21+
public class BranchingStepTests extends AbstractStepTestCase<BranchingStep> {
22+
23+
public void testPredicateNextStepChange() {
24+
String indexName = randomAlphaOfLength(5);
25+
ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metaData(MetaData.builder()
26+
.put(IndexMetaData.builder(indexName).settings(settings(Version.CURRENT))
27+
.numberOfShards(1).numberOfReplicas(0))).build();
28+
StepKey stepKey = new StepKey(randomAlphaOfLength(5), randomAlphaOfLength(5), BranchingStep.NAME);
29+
StepKey nextStepKey = new StepKey(randomAlphaOfLength(6), randomAlphaOfLength(6), BranchingStep.NAME);
30+
StepKey nextSkipKey = new StepKey(randomAlphaOfLength(7), randomAlphaOfLength(7), BranchingStep.NAME);
31+
{
32+
BranchingStep step = new BranchingStep(stepKey, nextStepKey, nextSkipKey, (i, c) -> true);
33+
expectThrows(UnsupportedOperationException.class, step::getNextStepKey);
34+
step.performAction(state.metaData().index(indexName).getIndex(), state);
35+
assertThat(step.getNextStepKey(), equalTo(step.getNextStepKeyOnTrue()));
36+
expectThrows(SetOnce.AlreadySetException.class, () -> step.performAction(state.metaData().index(indexName).getIndex(), state));
37+
}
38+
{
39+
BranchingStep step = new BranchingStep(stepKey, nextStepKey, nextSkipKey, (i, c) -> false);
40+
expectThrows(UnsupportedOperationException.class, step::getNextStepKey);
41+
step.performAction(state.metaData().index(indexName).getIndex(), state);
42+
assertThat(step.getNextStepKey(), equalTo(step.getNextStepKeyOnFalse()));
43+
expectThrows(SetOnce.AlreadySetException.class, () -> step.performAction(state.metaData().index(indexName).getIndex(), state));
44+
}
45+
}
46+
47+
@Override
48+
public BranchingStep createRandomInstance() {
49+
StepKey stepKey = new StepKey(randomAlphaOfLength(5), randomAlphaOfLength(5), BranchingStep.NAME);
50+
StepKey nextStepKey = new StepKey(randomAlphaOfLength(6), randomAlphaOfLength(6), BranchingStep.NAME);
51+
StepKey nextSkipKey = new StepKey(randomAlphaOfLength(7), randomAlphaOfLength(7), BranchingStep.NAME);
52+
return new BranchingStep(stepKey, nextStepKey, nextSkipKey, (i, c) -> randomBoolean());
53+
}
54+
55+
@Override
56+
public BranchingStep mutateInstance(BranchingStep instance) {
57+
StepKey key = instance.getKey();
58+
StepKey nextStepKey = instance.getNextStepKeyOnFalse();
59+
StepKey nextSkipStepKey = instance.getNextStepKeyOnTrue();
60+
BiPredicate<Index, ClusterState> predicate = instance.getPredicate();
61+
62+
switch (between(0, 2)) {
63+
case 0:
64+
key = new StepKey(key.getPhase(), key.getAction(), key.getName() + randomAlphaOfLength(5));
65+
break;
66+
case 1:
67+
nextStepKey = new StepKey(nextStepKey.getPhase(), nextStepKey.getAction(), nextStepKey.getName() + randomAlphaOfLength(5));
68+
break;
69+
case 2:
70+
nextSkipStepKey = new StepKey(nextSkipStepKey.getPhase(), nextSkipStepKey.getAction(),
71+
nextSkipStepKey.getName() + randomAlphaOfLength(5));
72+
break;
73+
default:
74+
throw new AssertionError("Illegal randomisation branch");
75+
}
76+
77+
return new BranchingStep(key, nextStepKey, nextSkipStepKey, predicate);
78+
}
79+
80+
@Override
81+
public BranchingStep copyInstance(BranchingStep instance) {
82+
return new BranchingStep(instance.getKey(), instance.getNextStepKeyOnFalse(), instance.getNextStepKeyOnTrue(),
83+
instance.getPredicate());
84+
}
85+
}

0 commit comments

Comments
 (0)