瀏覽代碼

Migrate to data tiers should always ensure a TIER_PREFERENCE is set (#79100)

Joe Gallo 4 年之前
父節點
當前提交
fd48faa1be

+ 8 - 8
server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java

@@ -39,14 +39,7 @@ import org.elasticsearch.common.regex.Regex;
 import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.settings.Setting.Property;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.xcontent.NamedObjectNotFoundException;
-import org.elasticsearch.xcontent.NamedXContentRegistry;
-import org.elasticsearch.xcontent.ToXContent;
-import org.elasticsearch.xcontent.ToXContentFragment;
-import org.elasticsearch.xcontent.XContentBuilder;
-import org.elasticsearch.xcontent.XContentFactory;
 import org.elasticsearch.common.xcontent.XContentHelper;
-import org.elasticsearch.xcontent.XContentParser;
 import org.elasticsearch.common.xcontent.XContentParserUtils;
 import org.elasticsearch.core.Nullable;
 import org.elasticsearch.gateway.MetadataStateFormat;
@@ -54,6 +47,13 @@ import org.elasticsearch.index.Index;
 import org.elasticsearch.index.IndexNotFoundException;
 import org.elasticsearch.plugins.MapperPlugin;
 import org.elasticsearch.rest.RestStatus;
+import org.elasticsearch.xcontent.NamedObjectNotFoundException;
+import org.elasticsearch.xcontent.NamedXContentRegistry;
+import org.elasticsearch.xcontent.ToXContent;
+import org.elasticsearch.xcontent.ToXContentFragment;
+import org.elasticsearch.xcontent.XContentBuilder;
+import org.elasticsearch.xcontent.XContentFactory;
+import org.elasticsearch.xcontent.XContentParser;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -427,7 +427,7 @@ public class Metadata implements Iterable<IndexMetadata>, Diffable<Metadata>, To
     /**
      * Finds the parent data streams, if any, for the specified concrete indices.
      */
-    public ImmutableOpenMap<String, IndexAbstraction.DataStream> findDataStreams(String[] concreteIndices) {
+    public ImmutableOpenMap<String, IndexAbstraction.DataStream> findDataStreams(String... concreteIndices) {
         assert concreteIndices != null;
         final ImmutableOpenMap.Builder<String, IndexAbstraction.DataStream> builder = ImmutableOpenMap.builder();
         final SortedMap<String, IndexAbstraction> lookup = getIndicesLookup();

+ 21 - 10
x-pack/plugin/ilm/qa/multi-node/src/javaRestTest/java/org/elasticsearch/xpack/MigrateToDataTiersIT.java

@@ -7,6 +7,9 @@
 
 package org.elasticsearch.xpack;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
 import org.apache.http.entity.ContentType;
 import org.apache.http.entity.StringEntity;
 import org.apache.http.util.EntityUtils;
@@ -36,8 +39,8 @@ import org.junit.AfterClass;
 import org.junit.Before;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
@@ -49,6 +52,7 @@ import static org.elasticsearch.xpack.TimeSeriesRestDriver.createPolicy;
 import static org.elasticsearch.xpack.TimeSeriesRestDriver.getOnlyIndexSettings;
 import static org.elasticsearch.xpack.TimeSeriesRestDriver.getStepKeyForIndex;
 import static org.hamcrest.Matchers.anEmptyMap;
+import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
@@ -134,9 +138,9 @@ public class MigrateToDataTiersIT extends ESRestTestCase {
         createNewSingletonPolicy(client(), rolloverOnlyPolicyName, "hot", new RolloverAction(null, null, null, 1L));
 
         String rolloverIndexPrefix = "rolloverpolicytest_index";
-        for (int i = 1; i < randomIntBetween(2, 5); i++) {
-            // assign the rollover-only policy to a few other indices - these indices and the rollover-only policy should not be migrated
-            // in any way
+        for (int i = 1; i <= 2; i++) {
+            // assign the rollover-only policy to a few other indices - these indices will end up getting caught by the catch-all
+            // tier preference migration
             createIndexWithSettings(client(), rolloverIndexPrefix + "-00000" + i, alias + i, Settings.builder()
                 .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
                 .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
@@ -167,10 +171,10 @@ public class MigrateToDataTiersIT extends ESRestTestCase {
         assertOK(migrateDeploymentResponse);
 
         Map<String, Object> migrateResponseAsMap = responseAsMap(migrateDeploymentResponse);
-        assertThat((ArrayList<String>) migrateResponseAsMap.get(MigrateToDataTiersResponse.MIGRATED_ILM_POLICIES.getPreferredName()),
-            containsInAnyOrder(policy));
-        assertThat((ArrayList<String>) migrateResponseAsMap.get(MigrateToDataTiersResponse.MIGRATED_INDICES.getPreferredName()),
-            containsInAnyOrder(index, indexWithDataWarmRouting));
+        assertThat((List<String>) migrateResponseAsMap.get(MigrateToDataTiersResponse.MIGRATED_ILM_POLICIES.getPreferredName()),
+            contains(policy));
+        assertThat((List<String>) migrateResponseAsMap.get(MigrateToDataTiersResponse.MIGRATED_INDICES.getPreferredName()),
+            containsInAnyOrder(index, indexWithDataWarmRouting, rolloverIndexPrefix + "-000001", rolloverIndexPrefix + "-000002"));
         assertThat(migrateResponseAsMap.get(MigrateToDataTiersResponse.REMOVED_LEGACY_TEMPLATE.getPreferredName()),
             is(templateName));
 
@@ -209,6 +213,13 @@ public class MigrateToDataTiersIT extends ESRestTestCase {
         assertThat(cachedPhaseDefinition, containsString(ShrinkAction.NAME));
         assertThat(cachedPhaseDefinition, containsString(SetPriorityAction.NAME));
         assertThat(cachedPhaseDefinition, containsString(ForceMergeAction.NAME));
+
+        // ENFORCE_DEFAULT_TIER_PREFERENCE has been set to true
+        Request getSettingsRequest = new Request("GET", "_cluster/settings");
+        Response getSettingsResponse = client().performRequest(getSettingsRequest);
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode json = mapper.readTree(getSettingsResponse.getEntity().getContent());
+        assertTrue(json.at("/persistent/cluster/routing/allocation/enforce_default_tier_preference").asBoolean());
     }
 
     @SuppressWarnings("unchecked")
@@ -274,9 +285,9 @@ public class MigrateToDataTiersIT extends ESRestTestCase {
 
         // response should contain the correct "to migrate" entities
         Map<String, Object> migrateResponseAsMap = responseAsMap(migrateDeploymentResponse);
-        assertThat((ArrayList<String>) migrateResponseAsMap.get(MigrateToDataTiersResponse.MIGRATED_ILM_POLICIES.getPreferredName()),
+        assertThat((List<String>) migrateResponseAsMap.get(MigrateToDataTiersResponse.MIGRATED_ILM_POLICIES.getPreferredName()),
             containsInAnyOrder(policy));
-        assertThat((ArrayList<String>) migrateResponseAsMap.get(MigrateToDataTiersResponse.MIGRATED_INDICES.getPreferredName()),
+        assertThat((List<String>) migrateResponseAsMap.get(MigrateToDataTiersResponse.MIGRATED_INDICES.getPreferredName()),
             containsInAnyOrder(index, indexWithDataWarmRouting));
         assertThat(migrateResponseAsMap.get(MigrateToDataTiersResponse.REMOVED_LEGACY_TEMPLATE.getPreferredName()),
             is(templateName));

+ 56 - 10
x-pack/plugin/ilm/src/main/java/org/elasticsearch/xpack/cluster/metadata/MetadataMigrateToDataTiersRoutingService.java

@@ -15,13 +15,13 @@ import org.elasticsearch.client.Client;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.cluster.metadata.Metadata;
+import org.elasticsearch.cluster.routing.allocation.DataTier;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.xcontent.NamedXContentRegistry;
 import org.elasticsearch.core.Nullable;
 import org.elasticsearch.core.Tuple;
 import org.elasticsearch.license.XPackLicenseState;
-import org.elasticsearch.cluster.routing.allocation.DataTier;
+import org.elasticsearch.xcontent.NamedXContentRegistry;
 import org.elasticsearch.xpack.core.ilm.AllocateAction;
 import org.elasticsearch.xpack.core.ilm.IndexLifecycleMetadata;
 import org.elasticsearch.xpack.core.ilm.LifecycleAction;
@@ -48,6 +48,7 @@ import java.util.stream.Collectors;
 import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_ROUTING_EXCLUDE_GROUP_SETTING;
 import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_ROUTING_INCLUDE_GROUP_SETTING;
 import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_SETTING;
+import static org.elasticsearch.cluster.routing.allocation.DataTier.ENFORCE_DEFAULT_TIER_PREFERENCE;
 import static org.elasticsearch.cluster.routing.allocation.DataTier.TIER_PREFERENCE;
 import static org.elasticsearch.xpack.core.ilm.LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY;
 import static org.elasticsearch.xpack.core.ilm.OperationMode.STOPPED;
@@ -127,6 +128,18 @@ public final class MetadataMigrateToDataTiersRoutingService {
         }
 
         Metadata.Builder mb = Metadata.builder(currentState.metadata());
+
+        // set ENFORCE_DEFAULT_TIER_PREFERENCE to true (in the persistent settings)
+        mb.persistentSettings(Settings.builder()
+            .put(mb.persistentSettings())
+            .put(ENFORCE_DEFAULT_TIER_PREFERENCE, true)
+            .build());
+
+        // and remove it from the transient settings, just in case it was there
+        Settings.Builder transientSettingsBuilder = Settings.builder().put(mb.transientSettings());
+        transientSettingsBuilder.remove(ENFORCE_DEFAULT_TIER_PREFERENCE);
+        mb.transientSettings(transientSettingsBuilder.build());
+
         String removedIndexTemplateName = null;
         if (Strings.hasText(indexTemplateToDelete)) {
             if (currentState.metadata().getTemplates().containsKey(indexTemplateToDelete)) {
@@ -376,20 +389,34 @@ public final class MetadataMigrateToDataTiersRoutingService {
         for (ObjectObjectCursor<String, IndexMetadata> index : currentState.metadata().indices()) {
             IndexMetadata indexMetadata = index.value;
             Settings currentSettings = indexMetadata.getSettings();
+
+            boolean removeNodeAttrIndexRoutingSettings = true;
+
+            // migrate using the `require` setting
             Settings newSettings = maybeMigrateRoutingSettingToTierPreference(nodeAttrIndexRequireRoutingSetting, indexMetadata);
+
             if (newSettings.equals(currentSettings)) {
-                // migrating based on the `require` setting was not successful so let's check if the index used the `include` routing
+                // migrating based on the `require` setting was not successful, so let's check if the index used the `include` routing
                 // setting to configure the allocations and try to migrate it
                 newSettings = maybeMigrateRoutingSettingToTierPreference(nodeAttrIndexIncludeRoutingSetting, indexMetadata);
             }
+            if (newSettings.equals(currentSettings)) {
+                removeNodeAttrIndexRoutingSettings = false;
+                // migrating based on the `include` setting was not successful,
+                // so, last stop, we just inject a tier preference regardless of anything else
+                newSettings = migrateToDefaultTierPreference(currentState, indexMetadata);
+            }
 
             if (newSettings.equals(currentSettings) == false) {
-                // we converted either the require or the include routing setting to tier preference
-                // so let's clear all the routing settings for the given attribute
                 Settings.Builder finalSettings = Settings.builder().put(newSettings);
-                finalSettings.remove(nodeAttrIndexExcludeRoutingSetting);
-                finalSettings.remove(nodeAttrIndexRequireRoutingSetting);
-                finalSettings.remove(nodeAttrIndexIncludeRoutingSetting);
+
+                if (removeNodeAttrIndexRoutingSettings) {
+                    // we converted either the `require` or the `include` routing setting to tier preference
+                    // so let's clear all the routing settings for the given attribute
+                    finalSettings.remove(nodeAttrIndexExcludeRoutingSetting);
+                    finalSettings.remove(nodeAttrIndexRequireRoutingSetting);
+                    finalSettings.remove(nodeAttrIndexIncludeRoutingSetting);
+                }
 
                 mb.put(IndexMetadata.builder(indexMetadata)
                     .settings(finalSettings)
@@ -413,9 +440,11 @@ public final class MetadataMigrateToDataTiersRoutingService {
         if (currentIndexSettings.keySet().contains(attributeBasedRoutingSettingName) == false) {
             return currentIndexSettings;
         }
-        // look at the value, get the correct tiers config and update the settings and index metadata
+
         Settings.Builder newSettingsBuilder = Settings.builder().put(currentIndexSettings);
         String indexName = indexMetadata.getIndex().getName();
+
+        // look at the value, get the correct tiers config and update the settings
         if (currentIndexSettings.keySet().contains(TIER_PREFERENCE)) {
             newSettingsBuilder.remove(attributeBasedRoutingSettingName);
             logger.debug("index [{}]: removed setting [{}]", indexName, attributeBasedRoutingSettingName);
@@ -428,7 +457,7 @@ public final class MetadataMigrateToDataTiersRoutingService {
                 newSettingsBuilder.remove(attributeBasedRoutingSettingName);
                 logger.debug("index [{}]: removed setting [{}]", indexName, attributeBasedRoutingSettingName);
                 logger.debug("index [{}]: configured setting [{}] to [{}]", indexName,
-                        TIER_PREFERENCE, convertedTierPreference);
+                    TIER_PREFERENCE, convertedTierPreference);
             } else {
                 // log warning and do *not* remove setting, return the settings unchanged
                 logger.warn("index [{}]: could not convert attribute based setting [{}] value of [{}] to a tier preference " +
@@ -440,6 +469,23 @@ public final class MetadataMigrateToDataTiersRoutingService {
         return newSettingsBuilder.build();
     }
 
+    private static Settings migrateToDefaultTierPreference(ClusterState currentState, IndexMetadata indexMetadata) {
+        Settings currentIndexSettings = indexMetadata.getSettings();
+        List<String> tierPreference = DataTier.parseTierList(currentIndexSettings.get(DataTier.TIER_PREFERENCE));
+        if (tierPreference.isEmpty() == false) {
+            return currentIndexSettings;
+        }
+
+        Settings.Builder newSettingsBuilder = Settings.builder().put(currentIndexSettings);
+        String indexName = indexMetadata.getIndex().getName();
+
+        boolean isDataStream = currentState.metadata().findDataStreams(indexName).isEmpty() == false;
+        String convertedTierPreference = isDataStream ? DataTier.DATA_HOT : DataTier.DATA_CONTENT;
+        newSettingsBuilder.put(TIER_PREFERENCE, convertedTierPreference);
+        logger.debug("index [{}]: configured setting [{}] to [{}]", indexName, TIER_PREFERENCE, convertedTierPreference);
+        return newSettingsBuilder.build();
+    }
+
     /**
      * Converts the provided node attribute value to the corresponding `_tier_preference` configuration.
      * Known (and convertible) attribute values are:

+ 109 - 82
x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/cluster/metadata/MetadataMigrateToDataTiersRoutingServiceTests.java

@@ -16,14 +16,15 @@ import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.cluster.metadata.IndexTemplateMetadata;
 import org.elasticsearch.cluster.metadata.Metadata;
 import org.elasticsearch.cluster.metadata.Template;
+import org.elasticsearch.cluster.routing.allocation.DataTier;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.xcontent.NamedXContentRegistry;
-import org.elasticsearch.xcontent.ParseField;
 import org.elasticsearch.common.xcontent.XContentHelper;
-import org.elasticsearch.xcontent.XContentType;
 import org.elasticsearch.core.TimeValue;
 import org.elasticsearch.core.Tuple;
 import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.xcontent.NamedXContentRegistry;
+import org.elasticsearch.xcontent.ParseField;
+import org.elasticsearch.xcontent.XContentType;
 import org.elasticsearch.xpack.cluster.metadata.MetadataMigrateToDataTiersRoutingService.MigratedEntities;
 import org.elasticsearch.xpack.core.ilm.AllocateAction;
 import org.elasticsearch.xpack.core.ilm.IndexLifecycleMetadata;
@@ -48,6 +49,7 @@ import java.util.Map;
 import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_ROUTING_EXCLUDE_GROUP_SETTING;
 import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_ROUTING_INCLUDE_GROUP_SETTING;
 import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_SETTING;
+import static org.elasticsearch.cluster.routing.allocation.DataTier.ENFORCE_DEFAULT_TIER_PREFERENCE;
 import static org.elasticsearch.cluster.routing.allocation.DataTier.TIER_PREFERENCE;
 import static org.elasticsearch.xpack.cluster.metadata.MetadataMigrateToDataTiersRoutingService.allocateActionDefinesRoutingRules;
 import static org.elasticsearch.xpack.cluster.metadata.MetadataMigrateToDataTiersRoutingService.convertAttributeValueToTierPreference;
@@ -56,6 +58,7 @@ import static org.elasticsearch.xpack.cluster.metadata.MetadataMigrateToDataTier
 import static org.elasticsearch.xpack.cluster.metadata.MetadataMigrateToDataTiersRoutingService.migrateToDataTiersRouting;
 import static org.elasticsearch.xpack.core.ilm.LifecycleExecutionState.ILM_CUSTOM_METADATA_KEY;
 import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItems;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.hamcrest.Matchers.nullValue;
@@ -97,9 +100,9 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
             coldAllocateAction);
 
         ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder()
-            .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
-                Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
-            .put(IndexMetadata.builder(indexName).settings(getBaseIndexSettings())).build())
+                .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
+                    Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
+                .put(IndexMetadata.builder(indexName).settings(getBaseIndexSettings())).build())
             .build();
 
         Metadata.Builder newMetadata = Metadata.builder(state.metadata());
@@ -138,9 +141,9 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
             randomNonNegativeLong(), randomNonNegativeLong());
 
         ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder()
-            .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
-                Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
-            .put(IndexMetadata.builder(indexName).settings(getBaseIndexSettings())).build())
+                .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
+                    Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
+                .put(IndexMetadata.builder(indexName).settings(getBaseIndexSettings())).build())
             .build();
 
         Metadata.Builder newMetadata = Metadata.builder(state.metadata());
@@ -153,7 +156,7 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
         LifecyclePolicy lifecyclePolicy = updatedLifecycleMetadata.getPolicies().get(lifecycleName);
         Map<String, LifecycleAction> warmActions = lifecyclePolicy.getPhases().get("warm").getActions();
         assertThat("allocate action in the warm phase didn't specify any number of replicas so it must be removed, together with the " +
-                "deactivated migrate action", warmActions.size(), is(1));
+            "deactivated migrate action", warmActions.size(), is(1));
         assertThat(warmActions.get(shrinkAction.getWriteableName()), is(shrinkAction));
     }
 
@@ -179,9 +182,9 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
                 .putCustom(ILM_CUSTOM_METADATA_KEY, preMigrationExecutionState.asMap());
 
             ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder()
-                .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
-                    Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
-                .put(indexMetadata).build())
+                    .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
+                        Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
+                    .put(indexMetadata).build())
                 .build();
 
             Metadata.Builder newMetadata = Metadata.builder(state.metadata());
@@ -217,9 +220,9 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
                 .putCustom(ILM_CUSTOM_METADATA_KEY, preMigrationExecutionState.asMap());
 
             ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder()
-                .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
-                    Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
-                .put(indexMetadata).build())
+                    .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
+                        Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
+                    .put(indexMetadata).build())
                 .build();
 
             Metadata.Builder newMetadata = Metadata.builder(state.metadata());
@@ -259,9 +262,9 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
                 .putCustom(ILM_CUSTOM_METADATA_KEY, preMigrationExecutionState.asMap());
 
             ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder()
-                .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
-                    Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
-                .put(indexMetadata).build())
+                    .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
+                        Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
+                    .put(indexMetadata).build())
                 .build();
 
             Metadata.Builder newMetadata = Metadata.builder(state.metadata());
@@ -299,9 +302,9 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
                 .putCustom(ILM_CUSTOM_METADATA_KEY, preMigrationExecutionState.asMap());
 
             ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder()
-                .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
-                    Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
-                .put(indexMetadata).build())
+                    .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
+                        Collections.singletonMap(policyMetadata.getName(), policyMetadata), OperationMode.STOPPED))
+                    .put(indexMetadata).build())
                 .build();
 
             Metadata.Builder newMetadata = Metadata.builder(state.metadata());
@@ -342,7 +345,7 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
         assertThat(allocateActionDefinesRoutingRules("data", new AllocateAction(null, null, Map.of("another_attribute", "rack1"), null,
             Map.of("data", "cold"))), is(true));
         assertThat(allocateActionDefinesRoutingRules("data", new AllocateAction(null, null, null, null, Map.of("another_attribute",
-            "cold"))),
+                "cold"))),
             is(false));
         assertThat(allocateActionDefinesRoutingRules("data", null), is(false));
     }
@@ -359,40 +362,40 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
     public void testMigrateIndices() {
         {
             // index with `warm` data attribute is migrated to the equivalent _tier_preference routing
-            IndexMetadata.Builder indexWitWarmDataAttribute =
-                IndexMetadata.builder("indexWitWarmDataAttribute").settings(getBaseIndexSettings().put(DATA_ROUTING_REQUIRE_SETTING,
-                    "warm"));
+            IndexMetadata.Builder indexWithWarmDataAttribute =
+                IndexMetadata.builder("indexWithWarmDataAttribute").settings(getBaseIndexSettings()
+                    .put(DATA_ROUTING_REQUIRE_SETTING, "warm"));
             ClusterState state =
-                ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder().put(indexWitWarmDataAttribute)).build();
+                ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder().put(indexWithWarmDataAttribute)).build();
 
             Metadata.Builder mb = Metadata.builder(state.metadata());
 
             List<String> migratedIndices = migrateIndices(mb, state, "data");
             assertThat(migratedIndices.size(), is(1));
-            assertThat(migratedIndices.get(0), is("indexWitWarmDataAttribute"));
+            assertThat(migratedIndices.get(0), is("indexWithWarmDataAttribute"));
 
             ClusterState migratedState = ClusterState.builder(ClusterName.DEFAULT).metadata(mb).build();
-            IndexMetadata migratedIndex = migratedState.metadata().index("indexWitWarmDataAttribute");
+            IndexMetadata migratedIndex = migratedState.metadata().index("indexWithWarmDataAttribute");
             assertThat(migratedIndex.getSettings().get(DATA_ROUTING_REQUIRE_SETTING), nullValue());
             assertThat(migratedIndex.getSettings().get(TIER_PREFERENCE), is("data_warm,data_hot"));
         }
 
         {
             // test the migration of the `include.data` configuration to the equivalent _tier_preference routing
-            IndexMetadata.Builder indexWitWarmDataAttribute =
-                IndexMetadata.builder("indexWitWarmDataAttribute").settings(getBaseIndexSettings().put(DATA_ROUTING_INCLUDE_SETTING,
-                    "warm"));
+            IndexMetadata.Builder indexWithWarmDataAttribute =
+                IndexMetadata.builder("indexWithWarmDataAttribute").settings(getBaseIndexSettings()
+                    .put(DATA_ROUTING_INCLUDE_SETTING, "warm"));
             ClusterState state =
-                ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder().put(indexWitWarmDataAttribute)).build();
+                ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder().put(indexWithWarmDataAttribute)).build();
 
             Metadata.Builder mb = Metadata.builder(state.metadata());
 
             List<String> migratedIndices = migrateIndices(mb, state, "data");
             assertThat(migratedIndices.size(), is(1));
-            assertThat(migratedIndices.get(0), is("indexWitWarmDataAttribute"));
+            assertThat(migratedIndices.get(0), is("indexWithWarmDataAttribute"));
 
             ClusterState migratedState = ClusterState.builder(ClusterName.DEFAULT).metadata(mb).build();
-            IndexMetadata migratedIndex = migratedState.metadata().index("indexWitWarmDataAttribute");
+            IndexMetadata migratedIndex = migratedState.metadata().index("indexWithWarmDataAttribute");
             assertThat(migratedIndex.getSettings().get(DATA_ROUTING_INCLUDE_SETTING), nullValue());
             assertThat(migratedIndex.getSettings().get(TIER_PREFERENCE), is("data_warm,data_hot"));
         }
@@ -404,8 +407,7 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
                 IndexMetadata.builder("indexWithTierPreferenceAndDataAttribute").settings(getBaseIndexSettings()
                     .put(DATA_ROUTING_REQUIRE_SETTING, "cold")
                     .put(DATA_ROUTING_INCLUDE_SETTING, "hot")
-                    .put(TIER_PREFERENCE, "data_warm,data_hot")
-                );
+                    .put(TIER_PREFERENCE, "data_warm,data_hot"));
             ClusterState state =
                 ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder().put(indexWithTierPreferenceAndDataAttribute)).build();
 
@@ -428,8 +430,7 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
             IndexMetadata.Builder indexWithTierPreferenceAndDataAttribute =
                 IndexMetadata.builder("indexWithTierPreferenceAndDataAttribute").settings(getBaseIndexSettings()
                     .put(DATA_ROUTING_INCLUDE_SETTING, "cold")
-                    .put(TIER_PREFERENCE, "data_warm,data_hot")
-                );
+                    .put(TIER_PREFERENCE, "data_warm,data_hot"));
             ClusterState state =
                 ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder().put(indexWithTierPreferenceAndDataAttribute)).build();
 
@@ -446,29 +447,30 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
         }
 
         {
-            // index with an unknown `data` attribute routing value should **not** be migrated
+            // index with an unknown `data` attribute routing value should be migrated
             IndexMetadata.Builder indexWithUnknownDataAttribute =
-                IndexMetadata.builder("indexWithUnknownDataAttribute").settings(getBaseIndexSettings().put(DATA_ROUTING_REQUIRE_SETTING,
-                    "something_else"));
+                IndexMetadata.builder("indexWithUnknownDataAttribute").settings(getBaseIndexSettings()
+                    .put(DATA_ROUTING_REQUIRE_SETTING, "something_else"));
             ClusterState state =
                 ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder().put(indexWithUnknownDataAttribute)).build();
 
             Metadata.Builder mb = Metadata.builder(state.metadata());
             List<String> migratedIndices = migrateIndices(mb, state, "data");
-            assertThat(migratedIndices.size(), is(0));
+            assertThat(migratedIndices.size(), is(1));
 
             ClusterState migratedState = ClusterState.builder(ClusterName.DEFAULT).metadata(mb).build();
             IndexMetadata migratedIndex = migratedState.metadata().index("indexWithUnknownDataAttribute");
             assertThat(migratedIndex.getSettings().get(DATA_ROUTING_REQUIRE_SETTING), is("something_else"));
+            assertThat(migratedIndex.getSettings().get(TIER_PREFERENCE), is("data_content"));
         }
 
         {
             // index with data and another attribute should only see the data attribute removed and the corresponding tier_preference
             // configured
             IndexMetadata.Builder indexDataAndBoxAttribute =
-                IndexMetadata.builder("indexWithDataAndBoxAttribute").settings(getBaseIndexSettings().put(DATA_ROUTING_REQUIRE_SETTING,
-                    "warm").put(BOX_ROUTING_REQUIRE_SETTING, "box1"));
-
+                IndexMetadata.builder("indexWithDataAndBoxAttribute").settings(getBaseIndexSettings()
+                    .put(DATA_ROUTING_REQUIRE_SETTING, "warm")
+                    .put(BOX_ROUTING_REQUIRE_SETTING, "box1"));
             ClusterState state =
                 ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder().put(indexDataAndBoxAttribute)).build();
 
@@ -485,40 +487,39 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
         }
 
         {
-            // index that doesn't have any data attribute routing but has another attribute should not see any change
+            // index that doesn't have any data attribute routing but has another attribute should be migrated
             IndexMetadata.Builder indexBoxAttribute =
-                IndexMetadata.builder("indexWithBoxAttribute").settings(getBaseIndexSettings().put(BOX_ROUTING_REQUIRE_SETTING, "warm"));
-
+                IndexMetadata.builder("indexWithBoxAttribute").settings(getBaseIndexSettings()
+                    .put(BOX_ROUTING_REQUIRE_SETTING, "warm"));
             ClusterState state =
                 ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder().put(indexBoxAttribute)).build();
 
             Metadata.Builder mb = Metadata.builder(state.metadata());
             List<String> migratedIndices = migrateIndices(mb, state, "data");
-            assertThat(migratedIndices.size(), is(0));
+            assertThat(migratedIndices.size(), is(1));
 
             ClusterState migratedState = ClusterState.builder(ClusterName.DEFAULT).metadata(mb).build();
             IndexMetadata migratedIndex = migratedState.metadata().index("indexWithBoxAttribute");
             assertThat(migratedIndex.getSettings().get(DATA_ROUTING_REQUIRE_SETTING), nullValue());
             assertThat(migratedIndex.getSettings().get(BOX_ROUTING_REQUIRE_SETTING), is("warm"));
-            assertThat(migratedIndex.getSettings().get(TIER_PREFERENCE), nullValue());
+            assertThat(migratedIndex.getSettings().get(TIER_PREFERENCE), is("data_content"));
         }
 
         {
             IndexMetadata.Builder indexNoRoutingAttribute =
                 IndexMetadata.builder("indexNoRoutingAttribute").settings(getBaseIndexSettings());
-
             ClusterState state =
                 ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder().put(indexNoRoutingAttribute)).build();
 
             Metadata.Builder mb = Metadata.builder(state.metadata());
             List<String> migratedIndices = migrateIndices(mb, state, "data");
-            assertThat(migratedIndices.size(), is(0));
+            assertThat(migratedIndices.size(), is(1));
 
             ClusterState migratedState = ClusterState.builder(ClusterName.DEFAULT).metadata(mb).build();
             IndexMetadata migratedIndex = migratedState.metadata().index("indexNoRoutingAttribute");
             assertThat(migratedIndex.getSettings().get(DATA_ROUTING_REQUIRE_SETTING), nullValue());
             assertThat(migratedIndex.getSettings().get(BOX_ROUTING_REQUIRE_SETTING), nullValue());
-            assertThat(migratedIndex.getSettings().get(TIER_PREFERENCE), nullValue());
+            assertThat(migratedIndex.getSettings().get(TIER_PREFERENCE), is("data_content"));
         }
     }
 
@@ -570,20 +571,21 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
         IndexMetadata.Builder indexWithUnknownDataAttribute =
             IndexMetadata.builder("indexWithUnknownDataAttribute").settings(getBaseIndexSettings().put(DATA_ROUTING_REQUIRE_SETTING,
                 "something_else"));
-        IndexMetadata.Builder indexWitWarmDataAttribute =
-            IndexMetadata.builder("indexWitWarmDataAttribute").settings(getBaseIndexSettings().put(DATA_ROUTING_REQUIRE_SETTING, "warm"));
+        IndexMetadata.Builder indexWithWarmDataAttribute =
+            IndexMetadata.builder("indexWithWarmDataAttribute").settings(getBaseIndexSettings().put(DATA_ROUTING_REQUIRE_SETTING, "warm"));
 
         ClusterState state = ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder()
-            .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
-                Map.of(policyToMigrate.getName(), policyWithDataAttribute, shouldntBeMigratedPolicy.getName(), policyWithOtherAttribute),
-                OperationMode.STOPPED))
-            .put(IndexTemplateMetadata.builder("catch-all").patterns(List.of("*"))
-                .settings(Settings.builder().put(DATA_ROUTING_REQUIRE_SETTING, "hot"))
-                .build())
-            .put(IndexTemplateMetadata.builder("other-template").patterns(List.of("other-*"))
-                .settings(Settings.builder().put(DATA_ROUTING_REQUIRE_SETTING, "hot"))
-                .build())
-            .put(indexWithUnknownDataAttribute).put(indexWitWarmDataAttribute))
+                .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
+                    Map.of(policyToMigrate.getName(), policyWithDataAttribute,
+                        shouldntBeMigratedPolicy.getName(), policyWithOtherAttribute),
+                    OperationMode.STOPPED))
+                .put(IndexTemplateMetadata.builder("catch-all").patterns(List.of("*"))
+                    .settings(Settings.builder().put(DATA_ROUTING_REQUIRE_SETTING, "hot"))
+                    .build())
+                .put(IndexTemplateMetadata.builder("other-template").patterns(List.of("other-*"))
+                    .settings(Settings.builder().put(DATA_ROUTING_REQUIRE_SETTING, "hot"))
+                    .build())
+                .put(indexWithUnknownDataAttribute).put(indexWithWarmDataAttribute))
             .build();
 
         {
@@ -594,8 +596,8 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
             assertThat(migratedEntities.removedIndexTemplateName, is("catch-all"));
             assertThat(migratedEntities.migratedPolicies.size(), is(1));
             assertThat(migratedEntities.migratedPolicies.get(0), is(lifecycleName));
-            assertThat(migratedEntities.migratedIndices.size(), is(1));
-            assertThat(migratedEntities.migratedIndices.get(0), is("indexWitWarmDataAttribute"));
+            assertThat(migratedEntities.migratedIndices.size(), is(2));
+            assertThat(migratedEntities.migratedIndices, hasItems("indexWithWarmDataAttribute", "indexWithUnknownDataAttribute"));
 
             ClusterState newState = migratedEntitiesTuple.v1();
             assertThat(newState.metadata().getTemplates().size(), is(1));
@@ -612,8 +614,8 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
             assertThat(migratedEntities.removedIndexTemplateName, nullValue());
             assertThat(migratedEntities.migratedPolicies.size(), is(1));
             assertThat(migratedEntities.migratedPolicies.get(0), is(lifecycleName));
-            assertThat(migratedEntities.migratedIndices.size(), is(1));
-            assertThat(migratedEntities.migratedIndices.get(0), is("indexWitWarmDataAttribute"));
+            assertThat(migratedEntities.migratedIndices.size(), is(2));
+            assertThat(migratedEntities.migratedIndices, hasItems("indexWithWarmDataAttribute", "indexWithUnknownDataAttribute"));
 
             ClusterState newState = migratedEntitiesTuple.v1();
             assertThat(newState.metadata().getTemplates().size(), is(2));
@@ -629,19 +631,22 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
             MigratedEntities migratedEntities = migratedEntitiesTuple.v2();
             assertThat(migratedEntities.migratedPolicies.size(), is(1));
             assertThat(migratedEntities.migratedPolicies.get(0), is(lifecycleName));
-            assertThat(migratedEntities.migratedIndices.size(), is(1));
-            assertThat(migratedEntities.migratedIndices.get(0), is("indexWitWarmDataAttribute"));
+            assertThat(migratedEntities.migratedIndices.size(), is(2));
+            assertThat(migratedEntities.migratedIndices, hasItems("indexWithWarmDataAttribute", "indexWithUnknownDataAttribute"));
 
-            IndexMetadata migratedIndex = migratedEntitiesTuple.v1().metadata().index("indexWitWarmDataAttribute");
+            IndexMetadata migratedIndex;
+            migratedIndex = migratedEntitiesTuple.v1().metadata().index("indexWithWarmDataAttribute");
             assertThat(migratedIndex.getSettings().get(TIER_PREFERENCE), is("data_warm,data_hot"));
+            migratedIndex = migratedEntitiesTuple.v1().metadata().index("indexWithUnknownDataAttribute");
+            assertThat(migratedIndex.getSettings().get(TIER_PREFERENCE), is("data_content"));
         }
     }
 
     public void testMigrateToDataTiersRoutingRequiresILMStopped() {
         {
             ClusterState ilmRunningState = ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder()
-                .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
-                    Map.of(), OperationMode.RUNNING)))
+                    .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
+                        Map.of(), OperationMode.RUNNING)))
                 .build();
             IllegalStateException illegalStateException = expectThrows(IllegalStateException.class,
                 () -> migrateToDataTiersRouting(ilmRunningState, "data", "catch-all", REGISTRY, client, null));
@@ -650,8 +655,8 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
 
         {
             ClusterState ilmStoppingState = ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder()
-                .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
-                    Map.of(), OperationMode.STOPPING)))
+                    .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
+                        Map.of(), OperationMode.STOPPING)))
                 .build();
             IllegalStateException illegalStateException = expectThrows(IllegalStateException.class,
                 () -> migrateToDataTiersRouting(ilmStoppingState, "data", "catch-all", REGISTRY, client, null));
@@ -660,8 +665,8 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
 
         {
             ClusterState ilmStoppedState = ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder()
-                .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
-                    Map.of(), OperationMode.STOPPED)))
+                    .putCustom(IndexLifecycleMetadata.TYPE, new IndexLifecycleMetadata(
+                        Map.of(), OperationMode.STOPPED)))
                 .build();
             Tuple<ClusterState, MigratedEntities> migratedState = migrateToDataTiersRouting(ilmStoppedState, "data", "catch-all",
                 REGISTRY, client, null);
@@ -679,7 +684,7 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
 
         String composableTemplateName = "catch-all-composable-template";
         ClusterState clusterState = ClusterState.builder(ClusterName.DEFAULT).metadata(Metadata.builder()
-            .put(composableTemplateName, composableIndexTemplate).build())
+                .put(composableTemplateName, composableIndexTemplate).build())
             .build();
         Tuple<ClusterState, MigratedEntities> migratedEntitiesTuple =
             migrateToDataTiersRouting(clusterState, "data", composableTemplateName, REGISTRY, client, null);
@@ -687,6 +692,28 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
         assertThat(migratedEntitiesTuple.v1().metadata().templatesV2().get(composableTemplateName), is(composableIndexTemplate));
     }
 
+    public void testMigrationSetsEnforceTierPreferenceToTrue() {
+        ClusterState clusterState;
+        Tuple<ClusterState, MigratedEntities> migratedEntitiesTuple;
+        Metadata.Builder metadata;
+
+        // if the cluster state doesn't mention the setting, it ends up true
+        clusterState = ClusterState.builder(ClusterName.DEFAULT).build();
+        migratedEntitiesTuple = migrateToDataTiersRouting(clusterState, null, null, REGISTRY, client, null);
+        assertTrue(DataTier.ENFORCE_DEFAULT_TIER_PREFERENCE_SETTING.get(migratedEntitiesTuple.v1().metadata().persistentSettings()));
+        assertFalse(migratedEntitiesTuple.v1().metadata().transientSettings().keySet().contains(DataTier.ENFORCE_DEFAULT_TIER_PREFERENCE));
+
+        // regardless of the true/false combinations of persistent/transient settings for ENFORCE_DEFAULT_TIER_PREFERENCE,
+        // it ends up set to true as a persistent setting (and if there was a transient setting, it was removed)
+        metadata = Metadata.builder();
+        metadata.persistentSettings(Settings.builder().put(ENFORCE_DEFAULT_TIER_PREFERENCE, randomBoolean()).build());
+        metadata.transientSettings(Settings.builder().put(ENFORCE_DEFAULT_TIER_PREFERENCE, randomBoolean()).build());
+        clusterState = ClusterState.builder(ClusterName.DEFAULT).metadata(metadata).build();
+        migratedEntitiesTuple = migrateToDataTiersRouting(clusterState, null, null, REGISTRY, client, null);
+        assertTrue(DataTier.ENFORCE_DEFAULT_TIER_PREFERENCE_SETTING.get(migratedEntitiesTuple.v1().metadata().persistentSettings()));
+        assertFalse(migratedEntitiesTuple.v1().metadata().transientSettings().keySet().contains(DataTier.ENFORCE_DEFAULT_TIER_PREFERENCE));
+    }
+
     private LifecyclePolicyMetadata getWarmColdPolicyMeta(SetPriorityAction setPriorityAction, ShrinkAction shrinkAction,
                                                           AllocateAction warmAllocateAction, AllocateAction coldAllocateAction) {
         LifecyclePolicy policy = new LifecyclePolicy(lifecycleName,
@@ -748,8 +775,8 @@ public class MetadataMigrateToDataTiersRoutingServiceTests extends ESTestCase {
     private Map<String, Object> getPhaseDefinitionAsMap(LifecycleExecutionState newLifecycleState) {
         XContentType entityContentType = XContentType.fromMediaType("application/json");
         return (Map<String, Object>) XContentHelper.convertToMap(entityContentType.xContent(),
-            new ByteArrayInputStream(newLifecycleState.getPhaseDefinition().getBytes(StandardCharsets.UTF_8)),
-            false)
+                new ByteArrayInputStream(newLifecycleState.getPhaseDefinition().getBytes(StandardCharsets.UTF_8)),
+                false)
             .get("phase_definition");
     }