diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateService.java index 4aa8e5dfa551e..19df34cd80fbf 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateService.java @@ -34,6 +34,7 @@ import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.set.Sets; +import org.elasticsearch.core.Assertions; import org.elasticsearch.core.Nullable; import org.elasticsearch.core.TimeValue; import org.elasticsearch.core.Tuple; @@ -818,7 +819,7 @@ void validateIndexTemplateV2(ProjectMetadata projectMetadata, String name, Compo var templateToValidate = indexTemplate.toBuilder().template(Template.builder(finalTemplate).settings(finalSettings)).build(); validate(name, templateToValidate, additionalSettings); - validateDataStreamsStillReferenced(projectMetadata, name, templateToValidate); + maybeValidateDataStreamsStillReferenced(projectMetadata, name, templateToValidate); validateLifecycle(componentTemplates, name, templateToValidate, globalRetentionSettings.get(false)); validateDataStreamOptions(componentTemplates, name, templateToValidate, globalRetentionSettings.get(true)); @@ -944,6 +945,43 @@ static void validateDataStreamOptions( } } + /** + * Maybe runs {@link #validateDataStreamsStillReferenced} if it looks like the new composite template could change data stream coverage. + */ + private static void maybeValidateDataStreamsStillReferenced( + ProjectMetadata project, + String templateName, + ComposableIndexTemplate newTemplate + ) { + final ComposableIndexTemplate existingTemplate = project.templatesV2().get(templateName); + final Settings existingSettings = Optional.ofNullable(existingTemplate) + .map(ComposableIndexTemplate::template) + .map(Template::settings) + .orElse(Settings.EMPTY); + final Settings newSettings = Optional.ofNullable(newTemplate) + .map(ComposableIndexTemplate::template) + .map(Template::settings) + .orElse(Settings.EMPTY); + // We check whether anything relevant has changed that could affect data stream coverage and return early if not. + // These checks are based on the implementation of findV2Template and the data stream template check in this method. + // If we're adding a new template, we do the full check in case this template's priority changes coverage. + if (existingTemplate != null + && Objects.equals(existingTemplate.indexPatterns(), newTemplate.indexPatterns()) + && Objects.equals(existingSettings.get(IndexMetadata.SETTING_INDEX_HIDDEN), newSettings.get(IndexMetadata.SETTING_INDEX_HIDDEN)) + && Objects.equals(existingTemplate.getDataStreamTemplate() != null, newTemplate.getDataStreamTemplate() != null) + && Objects.equals(existingTemplate.priorityOrZero(), newTemplate.priorityOrZero())) { + if (Assertions.ENABLED) { + try { + validateDataStreamsStillReferenced(project, templateName, newTemplate); + } catch (IllegalArgumentException e) { + assert false : "Data stream reference validation took a shortcut but the full check failed: " + e.getMessage(); + } + } + return; + } + validateDataStreamsStillReferenced(project, templateName, newTemplate); + } + /** * Validate that by changing or adding {@code newTemplate}, there are * no unreferenced data streams. Note that this scenario is still possible @@ -955,18 +993,16 @@ private static void validateDataStreamsStillReferenced( String templateName, ComposableIndexTemplate newTemplate ) { - final Set dataStreams = project.dataStreams() - .entrySet() - .stream() - .filter(entry -> entry.getValue().isSystem() == false) - .map(Map.Entry::getKey) - .collect(Collectors.toSet()); - Function, Set> findUnreferencedDataStreams = composableTemplates -> { final Set unreferenced = new HashSet<>(); // For each data stream that we have, see whether it's covered by a different // template (which is great), or whether it's now uncovered by any template - for (String dataStream : dataStreams) { + for (var dataStreamEntry : project.dataStreams().entrySet()) { + // Exclude system data streams + if (dataStreamEntry.getValue().isSystem()) { + continue; + } + final String dataStream = dataStreamEntry.getKey(); final String matchingTemplate = findV2Template(project, composableTemplates.entrySet(), dataStream, false, false); if (matchingTemplate == null) { unreferenced.add(dataStream);