You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by js...@apache.org on 2023/02/08 10:48:29 UTC

[unomi] 01/01: UNOMI-728 : add migration scripts to 2.2.0 version

This is an automated email from the ASF dual-hosted git repository.

jsinovassinnaik pushed a commit to branch UNOMI-728-index-migration
in repository https://gitbox.apache.org/repos/asf/unomi.git

commit 63203afe5f651f677b011e990dfe6e9b4a74f9fa
Author: jsinovassin <js...@jahia.com>
AuthorDate: Wed Feb 8 11:48:20 2023 +0100

    UNOMI-728 : add migration scripts to 2.2.0 version
---
 .../shell/migration/service/MigrationConfig.java   |  8 ++-
 .../shell/migration/utils/MigrationUtils.java      | 59 +++++++++++++----
 ...migrate-2.2.0-00-rolloverAndMigrateEvent.groovy | 74 ++++++++++++++++++++++
 ...grate-2.2.0-10-rolloverAndMigrateSession.groovy | 74 ++++++++++++++++++++++
 .../main/resources/org.apache.unomi.migration.cfg  |  3 +
 .../requestBody/2.2.0/base_index_mapping.json      | 30 +++++++++
 .../requestBody/2.2.0/base_reindex_request.json    |  8 +++
 .../2.2.0/create_rollover_policy_query.json        | 19 ++++++
 .../2.2.0/update_settings_poll_interval.json       |  5 ++
 9 files changed, 265 insertions(+), 15 deletions(-)

diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/service/MigrationConfig.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/service/MigrationConfig.java
index 16fb6728a..3d4ccd4c7 100644
--- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/service/MigrationConfig.java
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/service/MigrationConfig.java
@@ -46,7 +46,9 @@ public class MigrationConfig {
     public static final String MONTHLY_TOTAL_FIELDS_LIMIT = "monthlyIndex." + TOTAL_FIELDS_LIMIT;
     public static final String MONTHLY_MAX_DOC_VALUE_FIELDS_SEARCH = "monthlyIndex." + MAX_DOC_VALUE_FIELDS_SEARCH;
     public static final String MIGRATION_HISTORY_RECOVER = "recoverFromHistory";
-
+    public static final String ROLLOVER_MAX_AGE = "rolloverMaxAge";
+    public static final String ROLLOVER_MAX_SIZE = "rolloverMaxSize";
+    public static final String ROLLOVER_MAX_DOCS = "rolloverMaxDocs";
     protected static final Map<String, MigrationConfigProperty> configProperties;
     static {
         Map<String, MigrationConfigProperty> m = new HashMap<>();
@@ -65,6 +67,10 @@ public class MigrationConfig {
         m.put(MONTHLY_TOTAL_FIELDS_LIMIT, new MigrationConfigProperty("Enter ElasticSearch monthly index (event, session) mapping configuration: mapping.total_fields.limit (default: 1000): ", "1000"));
         m.put(MONTHLY_MAX_DOC_VALUE_FIELDS_SEARCH, new MigrationConfigProperty("Enter ElasticSearch monthly index (event, session) mapping configuration: max_docvalue_fields_search (default: 1000): ", "1000"));
         m.put(MIGRATION_HISTORY_RECOVER, new MigrationConfigProperty("We found an existing migration attempt, should we restart from it ? (this will avoid redoing steps already completed successfully) (yes/no)", null));
+        m.put(ROLLOVER_MAX_AGE, new MigrationConfigProperty("Enter ElasticSearch index rollover configuration: max_age (default: 365d): ", "365d"));
+        m.put(ROLLOVER_MAX_SIZE, new MigrationConfigProperty("Enter ElasticSearch index rollover configuration: max_size (default: null): ", null));
+        m.put(ROLLOVER_MAX_DOCS, new MigrationConfigProperty("Enter ElasticSearch index rollover configuration: max_docs (default: null): ", null));
+
         configProperties = Collections.unmodifiableMap(m);
     }
 
diff --git a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/utils/MigrationUtils.java b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/utils/MigrationUtils.java
index 40fd9f549..19bb6af2e 100644
--- a/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/utils/MigrationUtils.java
+++ b/tools/shell-commands/src/main/java/org/apache/unomi/shell/migration/utils/MigrationUtils.java
@@ -35,6 +35,7 @@ import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.Set;
+import java.util.StringJoiner;
 import java.util.stream.Collectors;
 
 import static org.apache.unomi.shell.migration.service.MigrationConfig.*;
@@ -77,8 +78,9 @@ public class MigrationUtils {
             String line;
             StringBuilder value = new StringBuilder();
             while ((line = br.readLine()) != null) {
-                if (!line.startsWith("/*") && !line.startsWith(" *") && !line.startsWith("*/"))
+                if (!line.startsWith("/*") && !line.startsWith(" *") && !line.startsWith("*/")) {
                     value.append(line);
+                }
             }
             in.close();
             return value.toString();
@@ -98,9 +100,7 @@ public class MigrationUtils {
         try (CloseableHttpResponse response = httpClient.execute(new HttpGet(esAddress + "/_aliases"))) {
             if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                 JSONObject indexesAsJson = new JSONObject(EntityUtils.toString(response.getEntity()));
-                return indexesAsJson.keySet().stream().
-                        filter(alias -> alias.startsWith(prefix)).
-                        collect(Collectors.toSet());
+                return indexesAsJson.keySet().stream().filter(alias -> alias.startsWith(prefix)).collect(Collectors.toSet());
             }
         }
         return Collections.emptySet();
@@ -131,8 +131,44 @@ public class MigrationUtils {
         return settings.replace("#mappings", mapping);
     }
 
-    public static void reIndex(CloseableHttpClient httpClient, BundleContext bundleContext, String esAddress, String indexName,
-                               String newIndexSettings, String painlessScript, MigrationContext migrationContext) throws Exception {
+    public static String buildIndexCreationRequestWithRollover(String baseIndexSettings, String mapping, MigrationContext context, String lifeCycleName, String rolloverAlias) throws IOException {
+        return buildIndexCreationRequest(baseIndexSettings, mapping, context, false)
+                .replace("#lifecycleName", lifeCycleName)
+                .replace("#lifecycleRolloverAlias", rolloverAlias);
+    }
+
+    public static String buildRolloverPolicyCreationRequest(String baseRequest, MigrationContext migrationContext) throws IOException {
+
+        StringJoiner rolloverHotActions = new StringJoiner(", ");
+
+        String rolloverMaxAge = migrationContext.getConfigString("rolloverMaxAge");
+        String rolloverMaxSize = migrationContext.getConfigString("rolloverMaxSize");
+        String rolloverMaxDocs = migrationContext.getConfigString("rolloverMaxDocs");
+        if (StringUtils.isNotBlank(rolloverMaxAge)) {
+            rolloverHotActions.add("\"max_age\": \"" + rolloverMaxAge + "\"");
+        }
+        if (StringUtils.isNotBlank(rolloverMaxSize)) {
+            rolloverHotActions.add("\"max_size\": \"" + rolloverMaxSize + "\"");
+        }
+        if (StringUtils.isNotBlank(rolloverMaxDocs)) {
+            rolloverHotActions.add("\"max_docs\": \"" + rolloverMaxDocs + "\"");
+        }
+        return baseRequest.replace("#rolloverHotActions", rolloverHotActions.toString());
+    }
+
+    public static void moveToIndex(CloseableHttpClient httpClient, BundleContext bundleContext, String esAddress, String sourceIndexName, String targetIndexName) throws Exception {
+        String reIndexRequest = resourceAsString(bundleContext, "requestBody/2.2.0/base_reindex_request.json").replace("#source", sourceIndexName).replace("#dest", targetIndexName);
+
+        HttpUtils.executePostRequest(httpClient, esAddress + "/_reindex", reIndexRequest, null);
+    }
+
+    public static void deleteIndex(CloseableHttpClient httpClient, String esAddress, String indexName) throws Exception {
+        if (indexExists(httpClient, esAddress, indexName)) {
+            HttpUtils.executeDeleteRequest(httpClient, esAddress + "/" + indexName, null);
+        }
+    }
+
+    public static void reIndex(CloseableHttpClient httpClient, BundleContext bundleContext, String esAddress, String indexName, String newIndexSettings, String painlessScript, MigrationContext migrationContext) throws Exception {
         if (indexName.endsWith("-cloned")) {
             // We should never reIndex a clone ...
             return;
@@ -140,9 +176,7 @@ public class MigrationUtils {
 
         String indexNameCloned = indexName + "-cloned";
 
-        String reIndexRequest = resourceAsString(bundleContext, "requestBody/2.0.0/base_reindex_request.json")
-                .replace("#source", indexNameCloned).replace("#dest", indexName)
-                .replace("#painless", StringUtils.isNotEmpty(painlessScript) ? getScriptPart(painlessScript) : "");
+        String reIndexRequest = resourceAsString(bundleContext, "requestBody/2.0.0/base_reindex_request.json").replace("#source", indexNameCloned).replace("#dest", indexName).replace("#painless", StringUtils.isNotEmpty(painlessScript) ? getScriptPart(painlessScript) : "");
 
         String setIndexReadOnlyRequest = resourceAsString(bundleContext, "requestBody/2.0.0/base_set_index_readonly_request.json");
 
@@ -208,10 +242,7 @@ public class MigrationUtils {
             }
 
             // scroll
-            response = HttpUtils.executePostRequest(httpClient, esAddress + "/_search/scroll", "{\n" +
-                    "  \"scroll_id\": \"" + scrollId + "\",\n" +
-                    "  \"scroll\": \"" + scrollDuration + "\"\n" +
-                    "}", null);
+            response = HttpUtils.executePostRequest(httpClient, esAddress + "/_search/scroll", "{\n" + "  \"scroll_id\": \"" + scrollId + "\",\n" + "  \"scroll\": \"" + scrollDuration + "\"\n" + "}", null);
         }
     }
 
@@ -222,7 +253,7 @@ public class MigrationUtils {
         while (true) {
             final JSONObject status = new JSONObject(HttpUtils.executeGetRequest(httpClient, esAddress + "/_cluster/health?wait_for_status=yellow&timeout=60s", null));
             if (!status.get("timed_out").equals("true")) {
-                migrationContext.printMessage("ES Cluster status is "  + status.get("status"));
+                migrationContext.printMessage("ES Cluster status is " + status.get("status"));
                 break;
             }
             migrationContext.printMessage("Waiting for ES Cluster status to be Yellow, current status is " + status.get("status"));
diff --git a/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.2.0-00-rolloverAndMigrateEvent.groovy b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.2.0-00-rolloverAndMigrateEvent.groovy
new file mode 100644
index 000000000..99fcf9e02
--- /dev/null
+++ b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.2.0-00-rolloverAndMigrateEvent.groovy
@@ -0,0 +1,74 @@
+import org.apache.unomi.shell.migration.service.MigrationContext
+import org.apache.unomi.shell.migration.utils.HttpUtils
+import org.apache.unomi.shell.migration.utils.MigrationUtils
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+MigrationContext context = migrationContext
+String esAddress = context.getConfigString("esAddress")
+String indexPrefix = context.getConfigString("indexPrefix")
+String newEventIndex = indexPrefix + "-event-000001"
+String rolloverPolicyName = indexPrefix + "-unomi-rollover-policy"
+String rolloverEventAlias = indexPrefix + "-event"
+
+
+context.performMigrationStep("2.2.0-00-update-lifecyle-poll-interval", () -> {
+    String updatePollIntervalBody = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.2.0/update_settings_poll_interval.json")
+            .replace("#pollIntervalValue", "\"2s\"")
+    HttpUtils.executePutRequest(context.getHttpClient(), esAddress + "/_cluster/settings", updatePollIntervalBody, null)
+})
+
+context.performMigrationStep("2.2.0-00-create-rollover-policy", () -> {
+    String createRolloverPolicyQuery = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.2.0/create_rollover_policy_query.json")
+    String rolloverQueryBody = MigrationUtils.buildRolloverPolicyCreationRequest(createRolloverPolicyQuery, context)
+
+    HttpUtils.executePutRequest(context.getHttpClient(), esAddress + "/_ilm/policy/" + rolloverPolicyName, rolloverQueryBody, null)
+})
+
+context.performMigrationStep("2.2.0-00-create-event-index", () -> {
+    if (!MigrationUtils.indexExists(context.getHttpClient(), esAddress, newEventIndex)) {
+        String baseRequest = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.2.0/base_index_mapping.json")
+        String mapping = MigrationUtils.extractMappingFromBundles(bundleContext, "event.json")
+
+        String newIndexSettings = MigrationUtils.buildIndexCreationRequestWithRollover(baseRequest, mapping, context, rolloverPolicyName, rolloverEventAlias)
+        HttpUtils.executePutRequest(context.getHttpClient(), esAddress + "/" + newEventIndex, newIndexSettings, null)
+    }
+})
+
+Set<String> eventIndices = MigrationUtils.getIndexesPrefixedBy(context.getHttpClient(), esAddress, indexPrefix + "-event-date-")
+List<String> sortedIndices = new ArrayList<>(eventIndices)
+Collections.sort(sortedIndices)
+
+context.performMigrationStep("2.2.0-00-migrate-existing-events", () -> {
+    sortedIndices.each { eventIndex ->
+        MigrationUtils.moveToIndex(context.getHttpClient(), bundleContext, esAddress, eventIndex, indexPrefix + "-event")
+        sleep(3000)
+    }
+})
+
+context.performMigrationStep("2.2.0-00-remove-old-events-indices", () -> {
+    sortedIndices.each { eventIndex ->
+        MigrationUtils.deleteIndex(context.getHttpClient(), esAddress, eventIndex)
+    }
+})
+
+context.performMigrationStep("2.2.0-00-reset-poll-interval", () -> {
+    String updatePollIntervalBody = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.2.0/update_settings_poll_interval.json")
+            .replace("#pollIntervalValue", "null")
+    HttpUtils.executePutRequest(context.getHttpClient(), esAddress + "/_cluster/settings", updatePollIntervalBody, null)
+})
diff --git a/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.2.0-10-rolloverAndMigrateSession.groovy b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.2.0-10-rolloverAndMigrateSession.groovy
new file mode 100644
index 000000000..a553ae808
--- /dev/null
+++ b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.2.0-10-rolloverAndMigrateSession.groovy
@@ -0,0 +1,74 @@
+import org.apache.unomi.shell.migration.service.MigrationContext
+import org.apache.unomi.shell.migration.utils.HttpUtils
+import org.apache.unomi.shell.migration.utils.MigrationUtils
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+MigrationContext context = migrationContext
+String esAddress = context.getConfigString("esAddress")
+String indexPrefix = context.getConfigString("indexPrefix")
+String newSessionIndex = indexPrefix + "-session-000001"
+String rolloverPolicyName = indexPrefix + "-unomi-rollover-policy"
+String rolloverSessionAlias = indexPrefix + "-session"
+
+
+context.performMigrationStep("2.2.0-10-update-lifecyle-poll-interval", () -> {
+    String updatePollIntervalBody = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.2.0/update_settings_poll_interval.json")
+            .replace("#pollIntervalValue", "\"2s\"")
+    HttpUtils.executePutRequest(context.getHttpClient(), esAddress + "/_cluster/settings", updatePollIntervalBody, null)
+})
+
+context.performMigrationStep("2.2.0-10-create-rollover-policy", () -> {
+    String createRolloverPolicyQuery = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.2.0/create_rollover_policy_query.json")
+    String rolloverQueryBody = MigrationUtils.buildRolloverPolicyCreationRequest(createRolloverPolicyQuery, context)
+
+    HttpUtils.executePutRequest(context.getHttpClient(), esAddress + "/_ilm/policy/" + rolloverPolicyName, rolloverQueryBody, null)
+})
+
+context.performMigrationStep("2.2.0-10-create-session-index", () -> {
+    if (!MigrationUtils.indexExists(context.getHttpClient(), esAddress, newSessionIndex)) {
+        String baseRequest = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.2.0/base_index_mapping.json")
+        String mapping = MigrationUtils.extractMappingFromBundles(bundleContext, "session.json")
+
+        String newIndexSettings = MigrationUtils.buildIndexCreationRequestWithRollover(baseRequest, mapping, context, rolloverPolicyName, rolloverSessionAlias)
+        HttpUtils.executePutRequest(context.getHttpClient(), esAddress + "/" + newSessionIndex, newIndexSettings, null)
+    }
+})
+
+Set<String> sessionIndices = MigrationUtils.getIndexesPrefixedBy(context.getHttpClient(), esAddress, indexPrefix + "-session-date-")
+List<String> sortedIndices = new ArrayList<>(sessionIndices)
+Collections.sort(sortedIndices)
+
+context.performMigrationStep("2.2.0-10-migrate-existing-sessions", () -> {
+    sortedIndices.each { sessionIndex ->
+        MigrationUtils.moveToIndex(context.getHttpClient(), bundleContext, esAddress, sessionIndex, indexPrefix + "-session")
+        sleep(3000)
+    }
+})
+
+context.performMigrationStep("2.2.0-10-remove-old-sessions-indices", () -> {
+    sortedIndices.each { sessionIndex ->
+        MigrationUtils.deleteIndex(context.getHttpClient(), esAddress, sessionIndex)
+    }
+})
+
+context.performMigrationStep("2.2.0-10-reset-poll-interval", () -> {
+    String updatePollIntervalBody = MigrationUtils.resourceAsString(bundleContext, "requestBody/2.2.0/update_settings_poll_interval.json")
+            .replace("#pollIntervalValue", "null")
+    HttpUtils.executePutRequest(context.getHttpClient(), esAddress + "/_cluster/settings", updatePollIntervalBody, null)
+})
diff --git a/tools/shell-commands/src/main/resources/org.apache.unomi.migration.cfg b/tools/shell-commands/src/main/resources/org.apache.unomi.migration.cfg
index f4cd52771..cc907d93f 100644
--- a/tools/shell-commands/src/main/resources/org.apache.unomi.migration.cfg
+++ b/tools/shell-commands/src/main/resources/org.apache.unomi.migration.cfg
@@ -32,6 +32,9 @@ number_of_shards=${org.apache.unomi.elasticsearch.defaultIndex.nbShards:-5}
 number_of_replicas=${org.apache.unomi.elasticsearch.defaultIndex.nbReplicas:-0}
 mapping.total_fields.limit=${org.apache.unomi.elasticsearch.defaultIndex.indexMappingTotalFieldsLimit:-1000}
 max_docvalue_fields_search=${org.apache.unomi.elasticsearch.defaultIndex.indexMaxDocValueFieldsSearch:-1000}
+rolloverMaxSize=${org.apache.unomi.elasticsearch.rollover.maxSize:-}
+rolloverMaxAge=${org.apache.unomi.elasticsearch.rollover.maxAge:-365d}
+rolloverMaxDocs=${org.apache.unomi.elasticsearch.rollover.maxDocs:-}
 
 # Should the migration try to recover from a previous run ?
 # (This allow to avoid redoing all the steps that would already succeeded on a previous attempt, that was stop or failed in the middle)
diff --git a/tools/shell-commands/src/main/resources/requestBody/2.2.0/base_index_mapping.json b/tools/shell-commands/src/main/resources/requestBody/2.2.0/base_index_mapping.json
new file mode 100644
index 000000000..c59422642
--- /dev/null
+++ b/tools/shell-commands/src/main/resources/requestBody/2.2.0/base_index_mapping.json
@@ -0,0 +1,30 @@
+{
+  "settings": {
+    "index": {
+      "number_of_shards": #numberOfShards,
+      "number_of_replicas": #numberOfReplicas,
+      "mapping.total_fields.limit": #mappingTotalFieldsLimit,
+      "max_docvalue_fields_search": #maxDocValueFieldsSearch,
+      "lifecycle.name": "#lifecycleName",
+      "lifecycle.rollover_alias": "#lifecycleRolloverAlias"
+    },
+    "analysis": {
+      "analyzer": {
+        "folding": {
+          "type": "custom",
+          "tokenizer": "keyword",
+          "filter": [
+            "lowercase",
+            "asciifolding"
+          ]
+        }
+      }
+    }
+  },
+  "aliases": {
+    "#lifecycleRolloverAlias": {
+      "is_write_index": true
+    }
+  },
+  "mappings": #mappings
+}
\ No newline at end of file
diff --git a/tools/shell-commands/src/main/resources/requestBody/2.2.0/base_reindex_request.json b/tools/shell-commands/src/main/resources/requestBody/2.2.0/base_reindex_request.json
new file mode 100644
index 000000000..ddcb79a5e
--- /dev/null
+++ b/tools/shell-commands/src/main/resources/requestBody/2.2.0/base_reindex_request.json
@@ -0,0 +1,8 @@
+{
+  "source": {
+    "index": "#source"
+  },
+  "dest": {
+    "index": "#dest"
+  }
+}
diff --git a/tools/shell-commands/src/main/resources/requestBody/2.2.0/create_rollover_policy_query.json b/tools/shell-commands/src/main/resources/requestBody/2.2.0/create_rollover_policy_query.json
new file mode 100644
index 000000000..c9bc94a29
--- /dev/null
+++ b/tools/shell-commands/src/main/resources/requestBody/2.2.0/create_rollover_policy_query.json
@@ -0,0 +1,19 @@
+{
+  "policy": {
+    "phases": {
+      "hot": {
+        "actions": {
+          "rollover": {
+            #rolloverHotActions
+          }
+        }
+      },
+      "delete": {
+        "min_age": "90d",
+        "actions": {
+          "delete": {}
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tools/shell-commands/src/main/resources/requestBody/2.2.0/update_settings_poll_interval.json b/tools/shell-commands/src/main/resources/requestBody/2.2.0/update_settings_poll_interval.json
new file mode 100644
index 000000000..75695e0fe
--- /dev/null
+++ b/tools/shell-commands/src/main/resources/requestBody/2.2.0/update_settings_poll_interval.json
@@ -0,0 +1,5 @@
+{
+  "persistent": {
+    "indices.lifecycle.poll_interval": #pollIntervalValue
+  }
+}
\ No newline at end of file