Skip to content

Commit 2a9c1f8

Browse files
Avoid race condition in ILMHistorySotre (#53039)
* Avoid race condition in ILMHistorySotre This change modifies ILMHistoryStore to always apply correct settings and mappings, even if template is deleted and not yet recreated. This ensures that ILM history index is correctly managed by ILM and also fixes flaky history tests that were prone to triggenring this race. This commit also refactors and simplifies ILM history tests. Closes #50353 and #52853 * Review comment Co-authored-by: Elastic Machine <[email protected]>
1 parent 1278960 commit 2a9c1f8

File tree

2 files changed

+24
-53
lines changed

2 files changed

+24
-53
lines changed

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

+8-50
Original file line numberDiff line numberDiff line change
@@ -1358,10 +1358,7 @@ public void testWaitForActiveShardsStep() throws Exception {
13581358
assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(PhaseCompleteStep.finalStep("hot").getKey())));
13591359
}
13601360

1361-
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/50353")
13621361
public void testHistoryIsWrittenWithSuccess() throws Exception {
1363-
String index = "success-index";
1364-
13651362
createNewSingletonPolicy("hot", new RolloverAction(null, null, 1L));
13661363
Request createIndexTemplate = new Request("PUT", "_template/rolling_indexes");
13671364
createIndexTemplate.setJsonEntity("{" +
@@ -1375,10 +1372,7 @@ public void testHistoryIsWrittenWithSuccess() throws Exception {
13751372
"}");
13761373
client().performRequest(createIndexTemplate);
13771374

1378-
createIndexWithSettings(index + "-1",
1379-
Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
1380-
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0),
1381-
true);
1375+
createIndexWithSettings(index + "-1", Settings.builder(), true);
13821376

13831377
// Index a document
13841378
index(client(), index + "-1", "1", "foo", "bar");
@@ -1396,69 +1390,34 @@ public void testHistoryIsWrittenWithSuccess() throws Exception {
13961390
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "wait-for-yellow-step"), 30, TimeUnit.SECONDS);
13971391
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "check-rollover-ready"), 30, TimeUnit.SECONDS);
13981392
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "attempt-rollover"), 30, TimeUnit.SECONDS);
1399-
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "update-rollover-lifecycle-date"), 30, TimeUnit.SECONDS);
14001393
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "set-indexing-complete"), 30, TimeUnit.SECONDS);
1401-
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "completed"), 30, TimeUnit.SECONDS);
1394+
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "complete"), 30, TimeUnit.SECONDS);
14021395

14031396
assertBusy(() -> assertHistoryIsPresent(policy, index + "-000002", true, "check-rollover-ready"), 30, TimeUnit.SECONDS);
14041397
}
14051398

1406-
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/50353")
14071399
public void testHistoryIsWrittenWithFailure() throws Exception {
1408-
String index = "failure-index";
1409-
1400+
createIndexWithSettings(index + "-1", Settings.builder(), false);
14101401
createNewSingletonPolicy("hot", new RolloverAction(null, null, 1L));
1411-
Request createIndexTemplate = new Request("PUT", "_template/rolling_indexes");
1412-
createIndexTemplate.setJsonEntity("{" +
1413-
"\"index_patterns\": [\""+ index + "-*\"], \n" +
1414-
" \"settings\": {\n" +
1415-
" \"number_of_shards\": 1,\n" +
1416-
" \"number_of_replicas\": 0,\n" +
1417-
" \"index.lifecycle.name\": \"" + policy+ "\"\n" +
1418-
" }\n" +
1419-
"}");
1420-
client().performRequest(createIndexTemplate);
1421-
1422-
createIndexWithSettings(index + "-1",
1423-
Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
1424-
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0),
1425-
false);
1402+
updatePolicy(index + "-1", policy);
14261403

14271404
// Index a document
14281405
index(client(), index + "-1", "1", "foo", "bar");
14291406
Request refreshIndex = new Request("POST", "/" + index + "-1/_refresh");
14301407
client().performRequest(refreshIndex);
14311408

1432-
assertBusy(() -> assertThat(getStepKeyForIndex(index + "-1").getName(), equalTo(ErrorStep.NAME)));
1409+
assertBusy(() -> assertThat(getStepKeyForIndex(index + "-1").getName(), equalTo(ErrorStep.NAME)), 30, TimeUnit.SECONDS);
14331410

14341411
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", false, "ERROR"), 30, TimeUnit.SECONDS);
14351412
}
14361413

1437-
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/50353")
14381414
public void testHistoryIsWrittenWithDeletion() throws Exception {
1439-
String index = "delete-index";
1440-
1441-
createNewSingletonPolicy("delete", new DeleteAction());
1442-
Request createIndexTemplate = new Request("PUT", "_template/delete_indexes");
1443-
createIndexTemplate.setJsonEntity("{" +
1444-
"\"index_patterns\": [\""+ index + "\"], \n" +
1445-
" \"settings\": {\n" +
1446-
" \"number_of_shards\": 1,\n" +
1447-
" \"number_of_replicas\": 0,\n" +
1448-
" \"index.lifecycle.name\": \"" + policy+ "\"\n" +
1449-
" }\n" +
1450-
"}");
1451-
client().performRequest(createIndexTemplate);
1452-
14531415
// Index should be created and then deleted by ILM
14541416
createIndexWithSettings(index, Settings.builder(), false);
1417+
createNewSingletonPolicy("delete", new DeleteAction());
1418+
updatePolicy(index, policy);
14551419

1456-
assertBusy(() -> {
1457-
logger.info("--> checking for index deletion...");
1458-
Request existCheck = new Request("HEAD", "/" + index);
1459-
Response resp = client().performRequest(existCheck);
1460-
assertThat(resp.getStatusLine().getStatusCode(), equalTo(404));
1461-
});
1420+
assertBusy(() -> assertFalse(indexExists(index)));
14621421

14631422
assertBusy(() -> {
14641423
assertHistoryIsPresent(policy, index, true, "delete", "delete", "wait-for-shard-history-leases");
@@ -1593,7 +1552,6 @@ private void assertHistoryIsPresent(String policyName, String indexName, boolean
15931552
// This method should be called inside an assertBusy, it has no retry logic of its own
15941553
private void assertHistoryIsPresent(String policyName, String indexName, boolean success,
15951554
@Nullable String phase, @Nullable String action, String stepName) throws IOException {
1596-
assertOK(client().performRequest(new Request("POST", indexName + "/_refresh")));
15971555
logger.info("--> checking for history item [{}], [{}], success: [{}], phase: [{}], action: [{}], step: [{}]",
15981556
policyName, indexName, success, phase, action, stepName);
15991557
final Request historySearchRequest = new Request("GET", "ilm-history*/_search?expand_wildcards=all");

x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/ilm/history/ILMHistoryStore.java

+16-3
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@
2525
import org.elasticsearch.cluster.ClusterState;
2626
import org.elasticsearch.cluster.metadata.AliasOrIndex;
2727
import org.elasticsearch.cluster.service.ClusterService;
28+
import org.elasticsearch.common.bytes.BytesArray;
2829
import org.elasticsearch.common.settings.Settings;
2930
import org.elasticsearch.common.unit.ByteSizeUnit;
3031
import org.elasticsearch.common.unit.ByteSizeValue;
3132
import org.elasticsearch.common.unit.TimeValue;
3233
import org.elasticsearch.common.xcontent.ToXContent;
3334
import org.elasticsearch.common.xcontent.XContentBuilder;
3435
import org.elasticsearch.common.xcontent.XContentFactory;
36+
import org.elasticsearch.common.xcontent.XContentHelper;
37+
import org.elasticsearch.common.xcontent.XContentType;
3538
import org.elasticsearch.threadpool.ThreadPool;
3639

3740
import java.io.Closeable;
@@ -46,6 +49,7 @@
4649
import static org.elasticsearch.xpack.core.ClientHelper.INDEX_LIFECYCLE_ORIGIN;
4750
import static org.elasticsearch.xpack.core.ilm.LifecycleSettings.LIFECYCLE_HISTORY_INDEX_ENABLED_SETTING;
4851
import static org.elasticsearch.xpack.ilm.history.ILMHistoryTemplateRegistry.INDEX_TEMPLATE_VERSION;
52+
import static org.elasticsearch.xpack.ilm.history.ILMHistoryTemplateRegistry.TEMPLATE_ILM_HISTORY;
4953

5054
/**
5155
* The {@link ILMHistoryStore} handles indexing {@link ILMHistoryItem} documents into the
@@ -175,6 +179,7 @@ public void putAsync(ILMHistoryItem item) {
175179
* @param listener Called after the index has been created. `onResponse` called with `true` if the index was created,
176180
* `false` if it already existed.
177181
*/
182+
@SuppressWarnings("unchecked")
178183
static void ensureHistoryIndex(Client client, ClusterState state, ActionListener<Boolean> listener) {
179184
final String initialHistoryIndexName = ILM_HISTORY_INDEX_PREFIX + "000001";
180185
final AliasOrIndex ilmHistory = state.metaData().getAliasAndIndexLookup().get(ILM_HISTORY_ALIAS);
@@ -183,11 +188,19 @@ static void ensureHistoryIndex(Client client, ClusterState state, ActionListener
183188
if (ilmHistory == null && initialHistoryIndex == null) {
184189
// No alias or index exists with the expected names, so create the index with appropriate alias
185190
logger.debug("creating ILM history index [{}]", initialHistoryIndexName);
191+
192+
// Template below should be already defined as real index template but it can be deleted. To avoid race condition with its
193+
// recreation we apply settings and mappings ourselves
194+
byte[] templateBytes = TEMPLATE_ILM_HISTORY.loadBytes();
195+
Map<String, Object> templateAsMap = XContentHelper.convertToMap(new BytesArray(templateBytes, 0, templateBytes.length),
196+
false, XContentType.JSON).v2();
197+
186198
client.admin().indices().prepareCreate(initialHistoryIndexName)
199+
.setSettings((Map<String, ?>) templateAsMap.get("settings"))
200+
.setMapping((Map<String, Object>) templateAsMap.get("mappings"))
187201
.setWaitForActiveShards(1)
188-
.addAlias(new Alias(ILM_HISTORY_ALIAS)
189-
.writeIndex(true))
190-
.execute(new ActionListener<CreateIndexResponse>() {
202+
.addAlias(new Alias(ILM_HISTORY_ALIAS).writeIndex(true))
203+
.execute(new ActionListener<>() {
191204
@Override
192205
public void onResponse(CreateIndexResponse response) {
193206
listener.onResponse(true);

0 commit comments

Comments
 (0)