Skip to content

Commit

Permalink
[ML] Decouple ML template versioning from product version
Browse files Browse the repository at this point in the history
The ML index templates used to be versioned using the product
version. This won't work in serverless, so needs to be changed.

Most plugins have a separate version constant for their index
templates that started at 1 and is incremented manually when
any template changes. For ML, we've got used to _not_ having
to worry about index template versions, only mappings versions.
To try to continue this approach as far as possible, the new
strategy for ML index template versions is:

- Start with 10000000. This is because 8.11.0's ID is 8110099,
  and whatever new scheme we use must generate numbers bigger
  than this.
- Add on the mappings version constants for all index mappings
  for which we use index templates. This means that incrementing
  the mappings version is still sufficient to cause the templates
  to be reinstalled. It's only necessary to increment the base
  template version if something changes in a template that's _not_
  in the mappings, such as priority. This should be very rare.
  So the risk of forgetting to update the template versions when
  updating mappings is removed.
  • Loading branch information
droberts195 committed Sep 26, 2023
1 parent 0a91c24 commit a589f00
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public final class NotificationsIndex {

private static final String RESOURCE_PATH = "/ml/";
private static final String MAPPINGS_VERSION_VARIABLE = "xpack.ml.version";
private static final int NOTIFICATIONS_INDEX_MAPPINGS_VERSION = 1;
public static final int NOTIFICATIONS_INDEX_MAPPINGS_VERSION = 1;

private NotificationsIndex() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,15 @@ public static String replaceVariable(String input, String variable, String value
* Checks if a versioned template exists, and if it exists checks if the version is greater than or equal to the current version.
* @param templateName Name of the index template
* @param state Cluster state
* @param currentVersion The current version to check against
*/
public static boolean checkTemplateExistsAndVersionIsGTECurrentVersion(String templateName, ClusterState state) {
public static boolean checkTemplateExistsAndVersionIsGTECurrentVersion(String templateName, ClusterState state, long currentVersion) {
ComposableIndexTemplate templateMetadata = state.metadata().templatesV2().get(templateName);
if (templateMetadata == null) {
return false;
}

return templateMetadata.version() != null && templateMetadata.version() >= Version.CURRENT.id;
return templateMetadata.version() != null && templateMetadata.version() >= currentVersion;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1752,7 +1752,12 @@ public static boolean criticalTemplatesInstalled(ClusterState clusterState) {
// installs it if necessary
List<String> templateNames = List.of(STATE_INDEX_PREFIX, AnomalyDetectorsIndex.jobResultsIndexPrefix());
for (String templateName : templateNames) {
allPresent = allPresent && TemplateUtils.checkTemplateExistsAndVersionIsGTECurrentVersion(templateName, clusterState);
allPresent = allPresent
&& TemplateUtils.checkTemplateExistsAndVersionIsGTECurrentVersion(
templateName,
clusterState,
MlIndexTemplateRegistry.ML_INDEX_TEMPLATE_VERSION
);
}

return allPresent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/
package org.elasticsearch.xpack.ml;

import org.elasticsearch.Version;
import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
import org.elasticsearch.cluster.service.ClusterService;
Expand All @@ -29,6 +28,23 @@

public class MlIndexTemplateRegistry extends IndexTemplateRegistry {

/**
* The template version starts from 10000000 because up until 8.11.0 we
* used version IDs for template versioning, so the first detached
* version number needs to be higher than the version ID of 8.11.0.
* We add on the mappings version of each of the templates that has
* mappings. This will cause _all_ templates to get installed when the
* mappings for any single template change. However, this is better
* than the risk of potentially updating mappings without updating the
* template versions and hence not reinstalling the templates. Note that
* the state index has no mappings - its template basically just says
* this - hence there's no mappings version for the state index. Please
* add a comment with a reason each time the base number is incremented.
* 10000001: TODO - reason
*/
public static final int ML_INDEX_TEMPLATE_VERSION = 10000000 + AnomalyDetectorsIndex.RESULTS_INDEX_MAPPINGS_VERSION
+ NotificationsIndex.NOTIFICATIONS_INDEX_MAPPINGS_VERSION + MlStatsIndex.STATS_INDEX_MAPPINGS_VERSION;

private static final String ROOT_RESOURCE_PATH = "/ml/";
private static final String ANOMALY_DETECTION_PATH = ROOT_RESOURCE_PATH + "anomalydetection/";
private static final String VERSION_PATTERN = "xpack.ml.version";
Expand All @@ -42,51 +58,51 @@ public class MlIndexTemplateRegistry extends IndexTemplateRegistry {

private IndexTemplateConfig stateTemplate() {
Map<String, String> variables = new HashMap<>();
variables.put(VERSION_ID_PATTERN, String.valueOf(Version.CURRENT.id));
variables.put(VERSION_ID_PATTERN, String.valueOf(ML_INDEX_TEMPLATE_VERSION));
// In serverless a different version of "state_index_template.json" is shipped that won't substitute the ILM policy variable
variables.put(INDEX_LIFECYCLE_NAME, ML_SIZE_BASED_ILM_POLICY_NAME);
variables.put(INDEX_LIFECYCLE_ROLLOVER_ALIAS, AnomalyDetectorsIndex.jobStateIndexWriteAlias());

return new IndexTemplateConfig(
AnomalyDetectorsIndexFields.STATE_INDEX_PREFIX,
ANOMALY_DETECTION_PATH + "state_index_template.json",
Version.CURRENT.id,
ML_INDEX_TEMPLATE_VERSION,
VERSION_PATTERN,
variables
);
}

private static IndexTemplateConfig anomalyDetectionResultsTemplate() {
Map<String, String> variables = new HashMap<>();
variables.put(VERSION_ID_PATTERN, String.valueOf(Version.CURRENT.id));
variables.put(VERSION_ID_PATTERN, String.valueOf(ML_INDEX_TEMPLATE_VERSION));
variables.put("xpack.ml.anomalydetection.results.mappings", AnomalyDetectorsIndex.resultsMapping());

return new IndexTemplateConfig(
AnomalyDetectorsIndex.jobResultsIndexPrefix(),
ANOMALY_DETECTION_PATH + "results_index_template.json",
Version.CURRENT.id,
ML_INDEX_TEMPLATE_VERSION,
VERSION_PATTERN,
variables
);
}

private static IndexTemplateConfig notificationsTemplate() {
Map<String, String> variables = new HashMap<>();
variables.put(VERSION_ID_PATTERN, String.valueOf(Version.CURRENT.id));
variables.put(VERSION_ID_PATTERN, String.valueOf(ML_INDEX_TEMPLATE_VERSION));
variables.put("xpack.ml.notifications.mappings", NotificationsIndex.mapping());

return new IndexTemplateConfig(
NotificationsIndex.NOTIFICATIONS_INDEX,
ROOT_RESOURCE_PATH + "notifications_index_template.json",
Version.CURRENT.id,
ML_INDEX_TEMPLATE_VERSION,
VERSION_PATTERN,
variables
);
}

private IndexTemplateConfig statsTemplate() {
Map<String, String> variables = new HashMap<>();
variables.put(VERSION_ID_PATTERN, String.valueOf(Version.CURRENT.id));
variables.put(VERSION_ID_PATTERN, String.valueOf(ML_INDEX_TEMPLATE_VERSION));
variables.put("xpack.ml.stats.mappings", MlStatsIndex.mapping());
// In serverless a different version of "stats_index_template.json" is shipped that won't substitute the ILM policy variable
variables.put(INDEX_LIFECYCLE_NAME, ML_SIZE_BASED_ILM_POLICY_NAME);
Expand All @@ -95,7 +111,7 @@ private IndexTemplateConfig statsTemplate() {
return new IndexTemplateConfig(
MlStatsIndex.TEMPLATE_NAME,
ROOT_RESOURCE_PATH + "stats_index_template.json",
Version.CURRENT.id,
ML_INDEX_TEMPLATE_VERSION,
VERSION_PATTERN,
variables
);
Expand Down

0 comments on commit a589f00

Please sign in to comment.