Skip to content

Commit 4506b37

Browse files
authored
ILM: Skip rolling indexes that are already rolled (#47324) (#47592)
An index with an ILM policy that has a rollover action in one of the phases was rolled over when the ILM conditions dictated regardless if it was already rolled over (eg. manually after modifying an index template in order to force the creation of a new index that uses the new mappings). This changes this behaviour and has ILM check if the index it's about to roll has not been rolled over in the meantime. (cherry picked from commit 37d6106) Signed-off-by: Andrei Dan <[email protected]>
1 parent 36cabba commit 4506b37

File tree

5 files changed

+193
-14
lines changed

5 files changed

+193
-14
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/RolloverStep.java

+7
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ public void performAction(IndexMetaData indexMetaData, ClusterState currentClust
4949
return;
5050
}
5151

52+
if (indexMetaData.getRolloverInfos().get(rolloverAlias) != null) {
53+
logger.info("index [{}] was already rolled over for alias [{}], not attempting to roll over again",
54+
indexMetaData.getIndex().getName(), rolloverAlias);
55+
listener.onResponse(true);
56+
return;
57+
}
58+
5259
if (indexMetaData.getAliases().containsKey(rolloverAlias) == false) {
5360
listener.onFailure(new IllegalArgumentException(String.format(Locale.ROOT,
5461
"%s [%s] does not point to index [%s]", RolloverAction.LIFECYCLE_ROLLOVER_ALIAS, rolloverAlias,

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStep.java

+7
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ public void evaluateCondition(IndexMetaData indexMetaData, Listener listener) {
5353
return;
5454
}
5555

56+
if (indexMetaData.getRolloverInfos().get(rolloverAlias) != null) {
57+
logger.info("index [{}] was already rolled over for alias [{}], not attempting to roll over again",
58+
indexMetaData.getIndex().getName(), rolloverAlias);
59+
listener.onResponse(true, new WaitForRolloverReadyStep.EmptyInfo());
60+
return;
61+
}
62+
5663
// The order of the following checks is important in ways which may not be obvious.
5764

5865
// First, figure out if 1) The configured alias points to this index, and if so,

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/RolloverStepTests.java

+37
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
import org.apache.lucene.util.SetOnce;
99
import org.elasticsearch.Version;
1010
import org.elasticsearch.action.ActionListener;
11+
import org.elasticsearch.action.admin.indices.rollover.MaxSizeCondition;
12+
import org.elasticsearch.action.admin.indices.rollover.RolloverInfo;
1113
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
1214
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
1315
import org.elasticsearch.client.AdminClient;
1416
import org.elasticsearch.client.Client;
1517
import org.elasticsearch.client.IndicesAdminClient;
1618
import org.elasticsearch.cluster.metadata.AliasMetaData;
1719
import org.elasticsearch.cluster.metadata.IndexMetaData;
20+
import org.elasticsearch.common.unit.ByteSizeValue;
1821
import org.elasticsearch.xpack.core.ilm.Step.StepKey;
1922
import org.junit.Before;
2023
import org.mockito.Mockito;
@@ -25,6 +28,7 @@
2528
import java.util.Locale;
2629

2730
import static org.hamcrest.Matchers.equalTo;
31+
import static org.hamcrest.core.Is.is;
2832

2933
public class RolloverStepTests extends AbstractStepTestCase<RolloverStep> {
3034

@@ -154,6 +158,39 @@ public void onFailure(Exception e) {
154158
assertEquals(true, actionCompleted.get());
155159
}
156160

161+
public void testPerformActionSkipsRolloverForAlreadyRolledIndex() {
162+
String rolloverAlias = randomAlphaOfLength(5);
163+
IndexMetaData indexMetaData = IndexMetaData.builder(randomAlphaOfLength(10))
164+
.putAlias(AliasMetaData.builder(rolloverAlias))
165+
.settings(settings(Version.CURRENT).put(RolloverAction.LIFECYCLE_ROLLOVER_ALIAS, rolloverAlias))
166+
.putRolloverInfo(new RolloverInfo(rolloverAlias,
167+
Collections.singletonList(new MaxSizeCondition(new ByteSizeValue(2L))),
168+
System.currentTimeMillis())
169+
)
170+
.numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
171+
172+
RolloverStep step = createRandomInstance();
173+
AdminClient adminClient = Mockito.mock(AdminClient.class);
174+
IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class);
175+
Mockito.when(client.admin()).thenReturn(adminClient);
176+
Mockito.when(adminClient.indices()).thenReturn(indicesClient);
177+
178+
step.performAction(indexMetaData, null, null, new AsyncActionStep.Listener() {
179+
180+
@Override
181+
public void onResponse(boolean complete) {
182+
assertThat(complete, is(true));
183+
}
184+
185+
@Override
186+
public void onFailure(Exception e) {
187+
throw new AssertionError("Unexpected method call", e);
188+
}
189+
});
190+
191+
Mockito.verify(indicesClient, Mockito.never()).rolloverIndex(Mockito.any(), Mockito.any());
192+
}
193+
157194
public void testPerformActionFailure() {
158195
String alias = randomAlphaOfLength(5);
159196
IndexMetaData indexMetaData = IndexMetaData.builder(randomAlphaOfLength(10))

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ilm/WaitForRolloverReadyStepTests.java

+69
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.elasticsearch.action.admin.indices.rollover.MaxAgeCondition;
1414
import org.elasticsearch.action.admin.indices.rollover.MaxDocsCondition;
1515
import org.elasticsearch.action.admin.indices.rollover.MaxSizeCondition;
16+
import org.elasticsearch.action.admin.indices.rollover.RolloverInfo;
1617
import org.elasticsearch.action.admin.indices.rollover.RolloverRequest;
1718
import org.elasticsearch.action.admin.indices.rollover.RolloverResponse;
1819
import org.elasticsearch.client.AdminClient;
@@ -29,13 +30,15 @@
2930
import org.mockito.invocation.InvocationOnMock;
3031
import org.mockito.stubbing.Answer;
3132

33+
import java.util.Collections;
3234
import java.util.HashSet;
3335
import java.util.Locale;
3436
import java.util.Map;
3537
import java.util.Set;
3638
import java.util.stream.Collectors;
3739

3840
import static org.hamcrest.Matchers.equalTo;
41+
import static org.hamcrest.Matchers.is;
3942

4043
public class WaitForRolloverReadyStepTests extends AbstractStepTestCase<WaitForRolloverReadyStep> {
4144

@@ -173,6 +176,64 @@ public void onFailure(Exception e) {
173176
Mockito.verify(indicesClient, Mockito.only()).rolloverIndex(Mockito.any(), Mockito.any());
174177
}
175178

179+
public void testEvaluateDoesntTriggerRolloverForIndexManuallyRolledOnLifecycleRolloverAlias() {
180+
String rolloverAlias = randomAlphaOfLength(5);
181+
IndexMetaData indexMetaData = IndexMetaData.builder(randomAlphaOfLength(10))
182+
.putAlias(AliasMetaData.builder(rolloverAlias))
183+
.settings(settings(Version.CURRENT).put(RolloverAction.LIFECYCLE_ROLLOVER_ALIAS, rolloverAlias))
184+
.putRolloverInfo(new RolloverInfo(rolloverAlias, Collections.singletonList(new MaxSizeCondition(new ByteSizeValue(2L))),
185+
System.currentTimeMillis()))
186+
.numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
187+
188+
WaitForRolloverReadyStep step = createRandomInstance();
189+
IndicesAdminClient indicesClient = indicesAdminClientMock();
190+
191+
step.evaluateCondition(indexMetaData, new AsyncWaitStep.Listener() {
192+
193+
@Override
194+
public void onResponse(boolean complete, ToXContentObject informationContext) {
195+
assertThat(complete, is(true));
196+
}
197+
198+
@Override
199+
public void onFailure(Exception e) {
200+
throw new AssertionError("Unexpected method call", e);
201+
}
202+
});
203+
204+
Mockito.verify(indicesClient, Mockito.never()).rolloverIndex(Mockito.any(), Mockito.any());
205+
}
206+
207+
public void testEvaluateTriggersRolloverForIndexManuallyRolledOnDifferentAlias() {
208+
String rolloverAlias = randomAlphaOfLength(5);
209+
IndexMetaData indexMetaData = IndexMetaData.builder(randomAlphaOfLength(10))
210+
.putAlias(AliasMetaData.builder(rolloverAlias))
211+
.settings(settings(Version.CURRENT).put(RolloverAction.LIFECYCLE_ROLLOVER_ALIAS, rolloverAlias))
212+
.putRolloverInfo(new RolloverInfo(randomAlphaOfLength(5),
213+
Collections.singletonList(new MaxSizeCondition(new ByteSizeValue(2L))),
214+
System.currentTimeMillis())
215+
)
216+
.numberOfShards(randomIntBetween(1, 5)).numberOfReplicas(randomIntBetween(0, 5)).build();
217+
218+
WaitForRolloverReadyStep step = createRandomInstance();
219+
IndicesAdminClient indicesClient = indicesAdminClientMock();
220+
221+
step.evaluateCondition(indexMetaData, new AsyncWaitStep.Listener() {
222+
223+
@Override
224+
public void onResponse(boolean complete, ToXContentObject informationContext) {
225+
assertThat(complete, is(true));
226+
}
227+
228+
@Override
229+
public void onFailure(Exception e) {
230+
throw new AssertionError("Unexpected method call", e);
231+
}
232+
});
233+
234+
Mockito.verify(indicesClient, Mockito.only()).rolloverIndex(Mockito.any(), Mockito.any());
235+
}
236+
176237
public void testPerformActionWithIndexingComplete() {
177238
String alias = randomAlphaOfLength(5);
178239
IndexMetaData indexMetaData = IndexMetaData.builder(randomAlphaOfLength(10))
@@ -399,4 +460,12 @@ public void onFailure(Exception e) {
399460
"%s [%s] does not point to index [%s]", RolloverAction.LIFECYCLE_ROLLOVER_ALIAS, alias,
400461
indexMetaData.getIndex().getName())));
401462
}
463+
464+
private IndicesAdminClient indicesAdminClientMock() {
465+
AdminClient adminClient = Mockito.mock(AdminClient.class);
466+
IndicesAdminClient indicesClient = Mockito.mock(IndicesAdminClient.class);
467+
Mockito.when(client.admin()).thenReturn(adminClient);
468+
Mockito.when(adminClient.indices()).thenReturn(indicesClient);
469+
return indicesClient;
470+
}
402471
}

x-pack/plugin/ilm/qa/multi-node/src/test/java/org/elasticsearch/xpack/ilm/TimeSeriesLifecycleActionsIT.java

+73-14
Original file line numberDiff line numberDiff line change
@@ -769,20 +769,6 @@ public void testRemoveAndReaddPolicy() throws Exception {
769769
client().performRequest(addPolicyRequest);
770770
assertBusy(() -> assertTrue((boolean) explainIndex(originalIndex).getOrDefault("managed", false)));
771771

772-
// Wait for rollover to error
773-
assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(new StepKey("hot", RolloverAction.NAME, ErrorStep.NAME))));
774-
775-
// Set indexing complete
776-
Request setIndexingCompleteRequest = new Request("PUT", "/" + originalIndex + "/_settings");
777-
setIndexingCompleteRequest.setJsonEntity("{\n" +
778-
" \"index.lifecycle.indexing_complete\": true\n" +
779-
"}");
780-
client().performRequest(setIndexingCompleteRequest);
781-
782-
// Retry policy
783-
Request retryRequest = new Request("POST", "/" + originalIndex + "/_ilm/retry");
784-
client().performRequest(retryRequest);
785-
786772
// Wait for everything to be copacetic
787773
assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(TerminalPolicyStep.KEY)));
788774
}
@@ -884,6 +870,79 @@ public void testExplainFilters() throws Exception {
884870
});
885871
}
886872

873+
public void testILMRolloverOnManuallyRolledIndex() throws Exception {
874+
String originalIndex = index + "-000001";
875+
String secondIndex = index + "-000002";
876+
String thirdIndex = index + "-000003";
877+
878+
// Configure ILM to run every second
879+
Request updateLifecylePollSetting = new Request("PUT", "_cluster/settings");
880+
updateLifecylePollSetting.setJsonEntity("{" +
881+
" \"transient\": {\n" +
882+
"\"indices.lifecycle.poll_interval\" : \"1s\" \n" +
883+
" }\n" +
884+
"}");
885+
client().performRequest(updateLifecylePollSetting);
886+
887+
// Set up a policy with rollover
888+
createNewSingletonPolicy("hot", new RolloverAction(null, null, 2L));
889+
Request createIndexTemplate = new Request("PUT", "_template/rolling_indexes");
890+
createIndexTemplate.setJsonEntity("{" +
891+
"\"index_patterns\": [\""+ index + "-*\"], \n" +
892+
" \"settings\": {\n" +
893+
" \"number_of_shards\": 1,\n" +
894+
" \"number_of_replicas\": 0,\n" +
895+
" \"index.lifecycle.name\": \"" + policy+ "\", \n" +
896+
" \"index.lifecycle.rollover_alias\": \"alias\"\n" +
897+
" }\n" +
898+
"}");
899+
client().performRequest(createIndexTemplate);
900+
901+
createIndexWithSettings(
902+
originalIndex,
903+
Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
904+
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0),
905+
true
906+
);
907+
908+
// Index a document
909+
index(client(), originalIndex, "1", "foo", "bar");
910+
Request refreshOriginalIndex = new Request("POST", "/" + originalIndex + "/_refresh");
911+
client().performRequest(refreshOriginalIndex);
912+
913+
// Manual rollover
914+
Request rolloverRequest = new Request("POST", "/alias/_rollover");
915+
rolloverRequest.setJsonEntity("{\n" +
916+
" \"conditions\": {\n" +
917+
" \"max_docs\": \"1\"\n" +
918+
" }\n" +
919+
"}"
920+
);
921+
client().performRequest(rolloverRequest);
922+
assertBusy(() -> assertTrue(indexExists(secondIndex)));
923+
924+
// Index another document into the original index so the ILM rollover policy condition is met
925+
index(client(), originalIndex, "2", "foo", "bar");
926+
client().performRequest(refreshOriginalIndex);
927+
928+
// Wait for the rollover policy to execute
929+
assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(TerminalPolicyStep.KEY)));
930+
931+
// ILM should manage the second index after attempting (and skipping) rolling the original index
932+
assertBusy(() -> assertTrue((boolean) explainIndex(secondIndex).getOrDefault("managed", true)));
933+
934+
// index some documents to trigger an ILM rollover
935+
index(client(), "alias", "1", "foo", "bar");
936+
index(client(), "alias", "2", "foo", "bar");
937+
index(client(), "alias", "3", "foo", "bar");
938+
Request refreshSecondIndex = new Request("POST", "/" + secondIndex + "/_refresh");
939+
client().performRequest(refreshSecondIndex).getStatusLine();
940+
941+
// ILM should rollover the second index even though it skipped the first one
942+
assertBusy(() -> assertThat(getStepKeyForIndex(secondIndex), equalTo(TerminalPolicyStep.KEY)));
943+
assertBusy(() -> assertTrue(indexExists(thirdIndex)));
944+
}
945+
887946
private void createFullPolicy(TimeValue hotTime) throws IOException {
888947
Map<String, LifecycleAction> hotActions = new HashMap<>();
889948
hotActions.put(SetPriorityAction.NAME, new SetPriorityAction(100));

0 commit comments

Comments
 (0)