Skip to content

Commit 66f65e2

Browse files
authored
ILM introduce the check-ts-end-time-passed step (#100179) (#100197)
This introduces a new ILM step, the `check-ts-end-time-passed` step that'll wait for the `index.time_series.end_time` to lapse for TS indices before allowing ILM to proceed with the execution of the following actions: * downsample * forcemerge * readonly * searchable_snapshot * shrink TSDS indices are allowed to receive writes until the configured `index.time_series.end_time` time passes. This makes sure ILM doesn't block these indices from accepting writes prematurely. NOTE: documentation is added in a subsequent PR Fixes #99696
1 parent ce32b74 commit 66f65e2

File tree

19 files changed

+580
-133
lines changed

19 files changed

+580
-133
lines changed

docs/changelog/100179.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 100179
2+
summary: ILM introduce the `check-ts-end-time-passed` step
3+
area: ILM+SLM
4+
type: bug
5+
issues:
6+
- 99696

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.xpack.core.ilm.Step.StepKey;
2828

2929
import java.io.IOException;
30+
import java.time.Instant;
3031
import java.util.List;
3132
import java.util.Objects;
3233
import java.util.concurrent.TimeUnit;
@@ -133,6 +134,7 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey) {
133134
StepKey timeSeriesIndexCheckBranchKey = new StepKey(phase, NAME, CONDITIONAL_TIME_SERIES_CHECK_KEY);
134135
StepKey checkNotWriteIndex = new StepKey(phase, NAME, CheckNotDataStreamWriteIndexStep.NAME);
135136
StepKey waitForNoFollowerStepKey = new StepKey(phase, NAME, WaitForNoFollowersStep.NAME);
137+
StepKey waitTimeSeriesEndTimePassesKey = new StepKey(phase, NAME, WaitUntilTimeSeriesEndTimePassesStep.NAME);
136138
StepKey readOnlyKey = new StepKey(phase, NAME, ReadOnlyStep.NAME);
137139
StepKey cleanupDownsampleIndexKey = new StepKey(phase, NAME, CleanupTargetIndexStep.NAME);
138140
StepKey generateDownsampleIndexNameKey = new StepKey(phase, NAME, DownsamplePrepareLifeCycleStateStep.NAME);
@@ -161,8 +163,18 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey) {
161163
checkNotWriteIndex,
162164
waitForNoFollowerStepKey
163165
);
164-
WaitForNoFollowersStep waitForNoFollowersStep = new WaitForNoFollowersStep(waitForNoFollowerStepKey, readOnlyKey, client);
166+
WaitForNoFollowersStep waitForNoFollowersStep = new WaitForNoFollowersStep(
167+
waitForNoFollowerStepKey,
168+
waitTimeSeriesEndTimePassesKey,
169+
client
170+
);
165171

172+
WaitUntilTimeSeriesEndTimePassesStep waitUntilTimeSeriesEndTimeStep = new WaitUntilTimeSeriesEndTimePassesStep(
173+
waitTimeSeriesEndTimePassesKey,
174+
readOnlyKey,
175+
Instant::now,
176+
client
177+
);
166178
// Mark source index as read-only
167179
ReadOnlyStep readOnlyStep = new ReadOnlyStep(readOnlyKey, generateDownsampleIndexNameKey, client);
168180

@@ -245,6 +257,7 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey) {
245257
isTimeSeriesIndexBranchingStep,
246258
checkNotWriteIndexStep,
247259
waitForNoFollowersStep,
260+
waitUntilTimeSeriesEndTimeStep,
248261
readOnlyStep,
249262
cleanupDownsampleIndexStep,
250263
generateDownsampleIndexNameStep,

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.xpack.core.ilm.Step.StepKey;
2626

2727
import java.io.IOException;
28+
import java.time.Instant;
2829
import java.util.ArrayList;
2930
import java.util.List;
3031
import java.util.Objects;
@@ -119,6 +120,7 @@ public List<Step> toSteps(Client client, String phase, Step.StepKey nextStepKey)
119120

120121
StepKey preForceMergeBranchingKey = new StepKey(phase, NAME, CONDITIONAL_SKIP_FORCE_MERGE_STEP);
121122
StepKey checkNotWriteIndexKey = new StepKey(phase, NAME, CheckNotDataStreamWriteIndexStep.NAME);
123+
StepKey waitTimeSeriesEndTimePassesKey = new StepKey(phase, NAME, WaitUntilTimeSeriesEndTimePassesStep.NAME);
122124
StepKey readOnlyKey = new StepKey(phase, NAME, ReadOnlyAction.NAME);
123125

124126
StepKey closeKey = new StepKey(phase, NAME, CloseIndexStep.NAME);
@@ -154,7 +156,14 @@ public List<Step> toSteps(Client client, String phase, Step.StepKey nextStepKey)
154156
// Indices in this step key can skip the no-op step and jump directly to the step with closeKey/forcemergeKey key
155157
CheckNotDataStreamWriteIndexStep checkNotWriteIndexStep = new CheckNotDataStreamWriteIndexStep(
156158
checkNotWriteIndexKey,
157-
codecChange ? closeKey : forceMergeKey
159+
waitTimeSeriesEndTimePassesKey
160+
);
161+
162+
WaitUntilTimeSeriesEndTimePassesStep waitUntilTimeSeriesEndTimeStep = new WaitUntilTimeSeriesEndTimePassesStep(
163+
waitTimeSeriesEndTimePassesKey,
164+
codecChange ? closeKey : forceMergeKey,
165+
Instant::now,
166+
client
158167
);
159168

160169
// Indices already in this step key when upgrading need to know how to move forward but stop making the index
@@ -182,6 +191,7 @@ public List<Step> toSteps(Client client, String phase, Step.StepKey nextStepKey)
182191
List<Step> mergeSteps = new ArrayList<>();
183192
mergeSteps.add(conditionalSkipShrinkStep);
184193
mergeSteps.add(checkNotWriteIndexStep);
194+
mergeSteps.add(waitUntilTimeSeriesEndTimeStep);
185195
mergeSteps.add(noopStep);
186196

187197
if (codecChange) {

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.xpack.core.ilm.Step.StepKey;
1717

1818
import java.io.IOException;
19+
import java.time.Instant;
1920
import java.util.Arrays;
2021
import java.util.List;
2122

@@ -58,10 +59,20 @@ public boolean isSafeAction() {
5859
@Override
5960
public List<Step> toSteps(Client client, String phase, StepKey nextStepKey) {
6061
StepKey checkNotWriteIndex = new StepKey(phase, NAME, CheckNotDataStreamWriteIndexStep.NAME);
62+
StepKey waitTimeSeriesEndTimePassesKey = new StepKey(phase, NAME, WaitUntilTimeSeriesEndTimePassesStep.NAME);
6163
StepKey readOnlyKey = new StepKey(phase, NAME, NAME);
62-
CheckNotDataStreamWriteIndexStep checkNotWriteIndexStep = new CheckNotDataStreamWriteIndexStep(checkNotWriteIndex, readOnlyKey);
64+
CheckNotDataStreamWriteIndexStep checkNotWriteIndexStep = new CheckNotDataStreamWriteIndexStep(
65+
checkNotWriteIndex,
66+
waitTimeSeriesEndTimePassesKey
67+
);
68+
WaitUntilTimeSeriesEndTimePassesStep waitUntilTimeSeriesEndTimeStep = new WaitUntilTimeSeriesEndTimePassesStep(
69+
waitTimeSeriesEndTimePassesKey,
70+
readOnlyKey,
71+
Instant::now,
72+
client
73+
);
6374
ReadOnlyStep readOnlyStep = new ReadOnlyStep(readOnlyKey, nextStepKey, client);
64-
return Arrays.asList(checkNotWriteIndexStep, readOnlyStep);
75+
return Arrays.asList(checkNotWriteIndexStep, waitUntilTimeSeriesEndTimeStep, readOnlyStep);
6576
}
6677

6778
@Override

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.elasticsearch.xpack.core.searchablesnapshots.MountSearchableSnapshotRequest;
2727

2828
import java.io.IOException;
29+
import java.time.Instant;
2930
import java.util.ArrayList;
3031
import java.util.List;
3132
import java.util.Objects;
@@ -106,6 +107,7 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac
106107
StepKey preActionBranchingKey = new StepKey(phase, NAME, CONDITIONAL_SKIP_ACTION_STEP);
107108
StepKey checkNoWriteIndex = new StepKey(phase, NAME, CheckNotDataStreamWriteIndexStep.NAME);
108109
StepKey waitForNoFollowerStepKey = new StepKey(phase, NAME, WaitForNoFollowersStep.NAME);
110+
StepKey waitTimeSeriesEndTimePassesKey = new StepKey(phase, NAME, WaitUntilTimeSeriesEndTimePassesStep.NAME);
109111
StepKey forceMergeStepKey = new StepKey(phase, NAME, ForceMergeStep.NAME);
110112
StepKey waitForSegmentCountKey = new StepKey(phase, NAME, SegmentCountStep.NAME);
111113
StepKey skipGeneratingSnapshotKey = new StepKey(phase, NAME, CONDITIONAL_SKIP_GENERATE_AND_CLEAN);
@@ -200,7 +202,13 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac
200202
);
201203
WaitForNoFollowersStep waitForNoFollowersStep = new WaitForNoFollowersStep(
202204
waitForNoFollowerStepKey,
205+
waitTimeSeriesEndTimePassesKey,
206+
client
207+
);
208+
WaitUntilTimeSeriesEndTimePassesStep waitUntilTimeSeriesEndTimeStep = new WaitUntilTimeSeriesEndTimePassesStep(
209+
waitTimeSeriesEndTimePassesKey,
203210
skipGeneratingSnapshotKey,
211+
Instant::now,
204212
client
205213
);
206214

@@ -321,6 +329,7 @@ public List<Step> toSteps(Client client, String phase, StepKey nextStepKey, XPac
321329
steps.add(conditionalSkipActionStep);
322330
steps.add(checkNoWriteIndexStep);
323331
steps.add(waitForNoFollowersStep);
332+
steps.add(waitUntilTimeSeriesEndTimeStep);
324333
steps.add(skipGeneratingSnapshotStep);
325334
if (forceMergeIndex) {
326335
steps.add(forceMergeStep);

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.xpack.core.ilm.Step.StepKey;
2626

2727
import java.io.IOException;
28+
import java.time.Instant;
2829
import java.util.Arrays;
2930
import java.util.List;
3031
import java.util.Objects;
@@ -142,6 +143,7 @@ public List<Step> toSteps(Client client, String phase, Step.StepKey nextStepKey)
142143
StepKey preShrinkBranchingKey = new StepKey(phase, NAME, CONDITIONAL_SKIP_SHRINK_STEP);
143144
StepKey checkNotWriteIndex = new StepKey(phase, NAME, CheckNotDataStreamWriteIndexStep.NAME);
144145
StepKey waitForNoFollowerStepKey = new StepKey(phase, NAME, WaitForNoFollowersStep.NAME);
146+
StepKey waitTimeSeriesEndTimePassesKey = new StepKey(phase, NAME, WaitUntilTimeSeriesEndTimePassesStep.NAME);
145147
StepKey readOnlyKey = new StepKey(phase, NAME, ReadOnlyAction.NAME);
146148
StepKey checkTargetShardsCountKey = new StepKey(phase, NAME, CheckTargetShardsCountStep.NAME);
147149
StepKey cleanupShrinkIndexKey = new StepKey(phase, NAME, CleanupShrinkIndexStep.NAME);
@@ -197,7 +199,17 @@ public List<Step> toSteps(Client client, String phase, Step.StepKey nextStepKey)
197199
checkNotWriteIndex,
198200
waitForNoFollowerStepKey
199201
);
200-
WaitForNoFollowersStep waitForNoFollowersStep = new WaitForNoFollowersStep(waitForNoFollowerStepKey, readOnlyKey, client);
202+
WaitForNoFollowersStep waitForNoFollowersStep = new WaitForNoFollowersStep(
203+
waitForNoFollowerStepKey,
204+
waitTimeSeriesEndTimePassesKey,
205+
client
206+
);
207+
WaitUntilTimeSeriesEndTimePassesStep waitUntilTimeSeriesEndTimeStep = new WaitUntilTimeSeriesEndTimePassesStep(
208+
waitTimeSeriesEndTimePassesKey,
209+
readOnlyKey,
210+
Instant::now,
211+
client
212+
);
201213
ReadOnlyStep readOnlyStep = new ReadOnlyStep(readOnlyKey, checkTargetShardsCountKey, client);
202214
CheckTargetShardsCountStep checkTargetShardsCountStep = new CheckTargetShardsCountStep(
203215
checkTargetShardsCountKey,
@@ -271,6 +283,7 @@ public List<Step> toSteps(Client client, String phase, Step.StepKey nextStepKey)
271283
conditionalSkipShrinkStep,
272284
checkNotWriteIndexStep,
273285
waitForNoFollowersStep,
286+
waitUntilTimeSeriesEndTimeStep,
274287
readOnlyStep,
275288
checkTargetShardsCountStep,
276289
cleanupShrinkIndexStep,

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

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
import org.elasticsearch.common.unit.ByteSizeValue;
2424
import org.elasticsearch.core.TimeValue;
2525
import org.elasticsearch.index.Index;
26-
import org.elasticsearch.xcontent.ToXContentObject;
27-
import org.elasticsearch.xcontent.XContentBuilder;
26+
import org.elasticsearch.xpack.core.ilm.step.info.EmptyInfo;
2827

2928
import java.util.HashMap;
3029
import java.util.Locale;
@@ -272,17 +271,4 @@ public boolean equals(Object obj) {
272271
WaitForRolloverReadyStep other = (WaitForRolloverReadyStep) obj;
273272
return super.equals(obj) && Objects.equals(conditions, other.conditions);
274273
}
275-
276-
// We currently have no information to provide for this AsyncWaitStep, so this is an empty object
277-
private static final class EmptyInfo implements ToXContentObject {
278-
279-
static final EmptyInfo INSTANCE = new EmptyInfo();
280-
281-
private EmptyInfo() {}
282-
283-
@Override
284-
public XContentBuilder toXContent(XContentBuilder builder, Params params) {
285-
return builder;
286-
}
287-
}
288274
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
package org.elasticsearch.xpack.core.ilm;
8+
9+
import org.elasticsearch.client.internal.Client;
10+
import org.elasticsearch.cluster.metadata.IndexMetadata;
11+
import org.elasticsearch.cluster.metadata.Metadata;
12+
import org.elasticsearch.core.TimeValue;
13+
import org.elasticsearch.index.Index;
14+
import org.elasticsearch.index.IndexMode;
15+
import org.elasticsearch.index.IndexSettings;
16+
import org.elasticsearch.xpack.core.ilm.step.info.EmptyInfo;
17+
import org.elasticsearch.xpack.core.ilm.step.info.SingleMessageFieldInfo;
18+
19+
import java.time.Instant;
20+
import java.util.Locale;
21+
import java.util.function.Supplier;
22+
23+
/**
24+
* This {@link Step} waits until the {@link org.elasticsearch.index.IndexSettings#TIME_SERIES_END_TIME} passes for time series indices.
25+
* For regular indices this step doesn't wait at all and the condition is evaluated to true immediately.
26+
*
27+
* Note that this step doens't execute an async/transport action and is able to evaluate its condition based on the local information
28+
* available however, we want this step to be executed periodically using the `AsyncWaitStep` infrastructure.
29+
* The condition will be evaluated every {@link LifecycleSettings#LIFECYCLE_POLL_INTERVAL}.
30+
*/
31+
public class WaitUntilTimeSeriesEndTimePassesStep extends AsyncWaitStep {
32+
33+
public static final String NAME = "check-ts-end-time-passed";
34+
private final Supplier<Instant> nowSupplier;
35+
36+
public WaitUntilTimeSeriesEndTimePassesStep(StepKey key, StepKey nextStepKey, Supplier<Instant> nowSupplier, Client client) {
37+
super(key, nextStepKey, client);
38+
this.nowSupplier = nowSupplier;
39+
}
40+
41+
@Override
42+
public boolean isRetryable() {
43+
return true;
44+
}
45+
46+
@Override
47+
public void evaluateCondition(Metadata metadata, Index index, Listener listener, TimeValue masterTimeout) {
48+
IndexMetadata indexMetadata = metadata.index(index);
49+
assert indexMetadata != null
50+
: "the index metadata for index [" + index.getName() + "] must exist in the cluster state for step " + "[" + NAME + "]";
51+
52+
if (IndexSettings.MODE.get(indexMetadata.getSettings()) != IndexMode.TIME_SERIES) {
53+
// this index is not a time series index so no need to wait
54+
listener.onResponse(true, EmptyInfo.INSTANCE);
55+
return;
56+
}
57+
Instant configuredEndTime = IndexSettings.TIME_SERIES_END_TIME.get(indexMetadata.getSettings());
58+
assert configuredEndTime != null : "a time series index must have an end time configured but [" + index.getName() + "] does not";
59+
if (nowSupplier.get().isBefore(configuredEndTime)) {
60+
listener.onResponse(
61+
false,
62+
new SingleMessageFieldInfo(
63+
String.format(
64+
Locale.ROOT,
65+
"The [%s] setting for index [%s] is [%s]. Waiting until the index's time series end time lapses before"
66+
+ " proceeding with action [%s] as the index can still accept writes.",
67+
IndexSettings.TIME_SERIES_END_TIME.getKey(),
68+
index.getName(),
69+
configuredEndTime.toEpochMilli(),
70+
getKey().action()
71+
)
72+
)
73+
);
74+
return;
75+
}
76+
77+
listener.onResponse(true, EmptyInfo.INSTANCE);
78+
}
79+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.core.ilm.step.info;
9+
10+
import org.elasticsearch.xcontent.ToXContentObject;
11+
import org.elasticsearch.xcontent.XContentBuilder;
12+
13+
/**
14+
* An empty XContent object to indicate an ILM step is not providing any information.
15+
*/
16+
public final class EmptyInfo implements ToXContentObject {
17+
18+
public static final EmptyInfo INSTANCE = new EmptyInfo();
19+
20+
private EmptyInfo() {}
21+
22+
@Override
23+
public XContentBuilder toXContent(XContentBuilder builder, Params params) {
24+
return builder;
25+
}
26+
}

0 commit comments

Comments
 (0)