Skip to content

Commit 9251392

Browse files
Avoid race condition in ILMHistorySotre (elastic#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 elastic#50353 and elastic#52853 * Review comment Co-authored-by: Elastic Machine <[email protected]>
1 parent 4b528d9 commit 9251392

File tree

2 files changed

+24
-52
lines changed

2 files changed

+24
-52
lines changed

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

Lines changed: 8 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,10 +1367,7 @@ public void testWaitForActiveShardsStep() throws Exception {
13671367
assertBusy(() -> assertThat(getStepKeyForIndex(originalIndex), equalTo(PhaseCompleteStep.finalStep("hot").getKey())));
13681368
}
13691369

1370-
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/50353")
13711370
public void testHistoryIsWrittenWithSuccess() throws Exception {
1372-
String index = "success-index";
1373-
13741371
createNewSingletonPolicy("hot", new RolloverAction(null, null, 1L));
13751372
Request createIndexTemplate = new Request("PUT", "_template/rolling_indexes");
13761373
createIndexTemplate.setJsonEntity("{" +
@@ -1384,10 +1381,7 @@ public void testHistoryIsWrittenWithSuccess() throws Exception {
13841381
"}");
13851382
client().performRequest(createIndexTemplate);
13861383

1387-
createIndexWithSettings(index + "-1",
1388-
Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
1389-
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0),
1390-
true);
1384+
createIndexWithSettings(index + "-1", Settings.builder(), true);
13911385

13921386
// Index a document
13931387
index(client(), index + "-1", "1", "foo", "bar");
@@ -1405,69 +1399,34 @@ public void testHistoryIsWrittenWithSuccess() throws Exception {
14051399
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "wait-for-yellow-step"), 30, TimeUnit.SECONDS);
14061400
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "check-rollover-ready"), 30, TimeUnit.SECONDS);
14071401
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "attempt-rollover"), 30, TimeUnit.SECONDS);
1408-
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "update-rollover-lifecycle-date"), 30, TimeUnit.SECONDS);
14091402
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "set-indexing-complete"), 30, TimeUnit.SECONDS);
1410-
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "completed"), 30, TimeUnit.SECONDS);
1403+
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", true, "complete"), 30, TimeUnit.SECONDS);
14111404

14121405
assertBusy(() -> assertHistoryIsPresent(policy, index + "-000002", true, "check-rollover-ready"), 30, TimeUnit.SECONDS);
14131406
}
14141407

1415-
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/50353")
14161408
public void testHistoryIsWrittenWithFailure() throws Exception {
1417-
String index = "failure-index";
1418-
1409+
createIndexWithSettings(index + "-1", Settings.builder(), false);
14191410
createNewSingletonPolicy("hot", new RolloverAction(null, null, 1L));
1420-
Request createIndexTemplate = new Request("PUT", "_template/rolling_indexes");
1421-
createIndexTemplate.setJsonEntity("{" +
1422-
"\"index_patterns\": [\""+ index + "-*\"], \n" +
1423-
" \"settings\": {\n" +
1424-
" \"number_of_shards\": 1,\n" +
1425-
" \"number_of_replicas\": 0,\n" +
1426-
" \"index.lifecycle.name\": \"" + policy+ "\"\n" +
1427-
" }\n" +
1428-
"}");
1429-
client().performRequest(createIndexTemplate);
1430-
1431-
createIndexWithSettings(index + "-1",
1432-
Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
1433-
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0),
1434-
false);
1411+
updatePolicy(index + "-1", policy);
14351412

14361413
// Index a document
14371414
index(client(), index + "-1", "1", "foo", "bar");
14381415
Request refreshIndex = new Request("POST", "/" + index + "-1/_refresh");
14391416
client().performRequest(refreshIndex);
14401417

1441-
assertBusy(() -> assertThat(getStepKeyForIndex(index + "-1").getName(), equalTo(ErrorStep.NAME)));
1418+
assertBusy(() -> assertThat(getStepKeyForIndex(index + "-1").getName(), equalTo(ErrorStep.NAME)), 30, TimeUnit.SECONDS);
14421419

14431420
assertBusy(() -> assertHistoryIsPresent(policy, index + "-1", false, "ERROR"), 30, TimeUnit.SECONDS);
14441421
}
14451422

1446-
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/50353")
14471423
public void testHistoryIsWrittenWithDeletion() throws Exception {
1448-
String index = "delete-index";
1449-
1450-
createNewSingletonPolicy("delete", new DeleteAction());
1451-
Request createIndexTemplate = new Request("PUT", "_template/delete_indexes");
1452-
createIndexTemplate.setJsonEntity("{" +
1453-
"\"index_patterns\": [\""+ index + "\"], \n" +
1454-
" \"settings\": {\n" +
1455-
" \"number_of_shards\": 1,\n" +
1456-
" \"number_of_replicas\": 0,\n" +
1457-
" \"index.lifecycle.name\": \"" + policy+ "\"\n" +
1458-
" }\n" +
1459-
"}");
1460-
client().performRequest(createIndexTemplate);
1461-
14621424
// Index should be created and then deleted by ILM
14631425
createIndexWithSettings(index, Settings.builder(), false);
1426+
createNewSingletonPolicy("delete", new DeleteAction());
1427+
updatePolicy(index, policy);
14641428

1465-
assertBusy(() -> {
1466-
logger.info("--> checking for index deletion...");
1467-
Request existCheck = new Request("HEAD", "/" + index);
1468-
Response resp = client().performRequest(existCheck);
1469-
assertThat(resp.getStatusLine().getStatusCode(), equalTo(404));
1470-
});
1429+
assertBusy(() -> assertFalse(indexExists(index)));
14711430

14721431
assertBusy(() -> {
14731432
assertHistoryIsPresent(policy, index, true, "delete", "delete", "wait-for-shard-history-leases");

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

Lines changed: 16 additions & 3 deletions
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;
@@ -45,6 +48,7 @@
4548
import static org.elasticsearch.xpack.core.ClientHelper.INDEX_LIFECYCLE_ORIGIN;
4649
import static org.elasticsearch.xpack.core.ilm.LifecycleSettings.LIFECYCLE_HISTORY_INDEX_ENABLED_SETTING;
4750
import static org.elasticsearch.xpack.ilm.history.ILMHistoryTemplateRegistry.INDEX_TEMPLATE_VERSION;
51+
import static org.elasticsearch.xpack.ilm.history.ILMHistoryTemplateRegistry.TEMPLATE_ILM_HISTORY;
4852

4953
/**
5054
* The {@link ILMHistoryStore} handles indexing {@link ILMHistoryItem} documents into the
@@ -163,6 +167,7 @@ public void putAsync(ILMHistoryItem item) {
163167
* @param listener Called after the index has been created. `onResponse` called with `true` if the index was created,
164168
* `false` if it already existed.
165169
*/
170+
@SuppressWarnings("unchecked")
166171
static void ensureHistoryIndex(Client client, ClusterState state, ActionListener<Boolean> listener) {
167172
final String initialHistoryIndexName = ILM_HISTORY_INDEX_PREFIX + "000001";
168173
final AliasOrIndex ilmHistory = state.metaData().getAliasAndIndexLookup().get(ILM_HISTORY_ALIAS);
@@ -171,11 +176,19 @@ static void ensureHistoryIndex(Client client, ClusterState state, ActionListener
171176
if (ilmHistory == null && initialHistoryIndex == null) {
172177
// No alias or index exists with the expected names, so create the index with appropriate alias
173178
logger.debug("creating ILM history index [{}]", initialHistoryIndexName);
179+
180+
// Template below should be already defined as real index template but it can be deleted. To avoid race condition with its
181+
// recreation we apply settings and mappings ourselves
182+
byte[] templateBytes = TEMPLATE_ILM_HISTORY.loadBytes();
183+
Map<String, Object> templateAsMap = XContentHelper.convertToMap(new BytesArray(templateBytes, 0, templateBytes.length),
184+
false, XContentType.JSON).v2();
185+
174186
client.admin().indices().prepareCreate(initialHistoryIndexName)
187+
.setSettings((Map<String, ?>) templateAsMap.get("settings"))
188+
.setMapping((Map<String, Object>) templateAsMap.get("mappings"))
175189
.setWaitForActiveShards(1)
176-
.addAlias(new Alias(ILM_HISTORY_ALIAS)
177-
.writeIndex(true))
178-
.execute(new ActionListener<CreateIndexResponse>() {
190+
.addAlias(new Alias(ILM_HISTORY_ALIAS).writeIndex(true))
191+
.execute(new ActionListener<>() {
179192
@Override
180193
public void onResponse(CreateIndexResponse response) {
181194
listener.onResponse(true);

0 commit comments

Comments
 (0)