Skip to content

Commit 679beda

Browse files
authored
Add deprecation info and warnings for an empty TIER_PREFERENCE (#79305)
1 parent 159cb44 commit 679beda

File tree

11 files changed

+253
-67
lines changed

11 files changed

+253
-67
lines changed

server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -814,16 +814,26 @@ static Settings aggregateIndexSettings(ClusterState currentState, CreateIndexClu
814814
indexSettingsBuilder.put(requestSettings.build());
815815

816816
if (sourceMetadata == null) { // not for shrink/split/clone
817+
String currentTierPreference = indexSettingsBuilder.get(DataTier.TIER_PREFERENCE);
818+
List<String> preferredTiers = DataTier.parseTierList(currentTierPreference);
817819
if (enforceDefaultTierPreference) {
818820
// regardless of any previous logic, we're going to force there
819821
// to be an appropriate non-empty value for the tier preference
820-
String currentTierPreference = indexSettingsBuilder.get(DataTier.TIER_PREFERENCE);
821-
if (DataTier.parseTierList(currentTierPreference).isEmpty()) {
822+
if (preferredTiers.isEmpty()) {
822823
String newTierPreference = isDataStreamIndex ? DataTier.DATA_HOT : DataTier.DATA_CONTENT;
823824
logger.debug("enforcing default [{}] setting for [{}] creation, replacing [{}] with [{}]",
824825
DataTier.TIER_PREFERENCE, request.index(), currentTierPreference, newTierPreference);
825826
indexSettingsBuilder.put(DataTier.TIER_PREFERENCE, newTierPreference);
826827
}
828+
} else {
829+
// if we're not enforcing the tier preference, then maybe warn
830+
if (preferredTiers.isEmpty()) {
831+
if (DataTier.dataNodesWithoutAllDataRoles(currentState).isEmpty() == false) {
832+
DEPRECATION_LOGGER.warn(DeprecationCategory.INDICES, "index_without_tier_preference",
833+
"[{}] creating index with an empty [{}] setting, in 8.0 this setting will be required for all indices",
834+
request.index(), DataTier.TIER_PREFERENCE);
835+
}
836+
}
827837
}
828838
}
829839

server/src/main/java/org/elasticsearch/cluster/routing/allocation/DataTier.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import org.apache.logging.log4j.LogManager;
1212
import org.apache.logging.log4j.Logger;
13+
import org.elasticsearch.cluster.ClusterState;
1314
import org.elasticsearch.cluster.metadata.IndexMetadata;
1415
import org.elasticsearch.cluster.node.DiscoveryNode;
1516
import org.elasticsearch.cluster.node.DiscoveryNodeRole;
@@ -28,6 +29,7 @@
2829
import java.util.Map;
2930
import java.util.Set;
3031
import java.util.function.Function;
32+
import java.util.stream.Collectors;
3133

3234
/**
3335
* The {@code DataTier} class encapsulates the formalization of the "content",
@@ -266,4 +268,26 @@ public Iterator<Setting<?>> settings() {
266268
return dependencies.iterator();
267269
}
268270
}
271+
272+
/**
273+
* Checks each data node in the cluster state to see whether it has the explicit data role or if it has
274+
* all data tiers (e.g. 'data_hot', 'data_warm', etc). The former condition being treated as a shortcut
275+
* for the latter condition (see DataTierAllocationDecider#allocationAllowed(String, Set)) for details,
276+
* as well as the various DataTier#isFooNode(DiscoveryNode) methods.
277+
*
278+
* @param clusterState the cluster state
279+
* @return a set of data nodes that do not have all data roles
280+
*/
281+
public static Set<DiscoveryNode> dataNodesWithoutAllDataRoles(ClusterState clusterState) {
282+
return clusterState.getNodes().getDataNodes().values().stream()
283+
.filter(node -> {
284+
Set<String> roles = node.getRoles().stream()
285+
.map(DiscoveryNodeRole::roleName)
286+
.collect(Collectors.toSet());
287+
288+
return roles.contains(DiscoveryNodeRole.DATA_ROLE.roleName()) == false &&
289+
roles.containsAll(ALL_DATA_TIERS) == false;
290+
})
291+
.collect(Collectors.toSet());
292+
}
269293
}

server/src/test/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexServiceTests.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.elasticsearch.cluster.routing.RoutingTable;
2828
import org.elasticsearch.cluster.routing.allocation.AllocationService;
2929
import org.elasticsearch.cluster.routing.allocation.DataTier;
30+
import org.elasticsearch.cluster.routing.allocation.DataTierTests;
3031
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
3132
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
3233
import org.elasticsearch.cluster.routing.allocation.decider.MaxRetryAllocationDecider;
@@ -996,13 +997,24 @@ private Optional<String> aggregatedTierPreference(Settings settings, boolean isD
996997
} else {
997998
request.dataStreamName(null);
998999
}
999-
Settings aggregatedIndexSettings = aggregateIndexSettings(ClusterState.EMPTY_STATE, request, templateSettings,
1000+
1001+
ClusterState clusterState = ClusterState.EMPTY_STATE;
1002+
if (randomBoolean()) {
1003+
clusterState = DataTierTests.clusterStateWithoutAllDataRoles();
1004+
}
1005+
1006+
Settings aggregatedIndexSettings = aggregateIndexSettings(clusterState, request, templateSettings,
10001007
null, Settings.EMPTY, IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, randomShardLimitService(),
10011008
org.elasticsearch.core.Set.of(new DataTier.DefaultHotAllocationSettingProvider()), enforceDefaultTierPreference);
10021009

10031010
if (aggregatedIndexSettings.keySet().contains(DataTier.TIER_PREFERENCE)) {
10041011
return Optional.of(aggregatedIndexSettings.get(DataTier.TIER_PREFERENCE));
10051012
} else {
1013+
if (clusterState != ClusterState.EMPTY_STATE) {
1014+
assertWarnings("[" + request.index() + "] creating index with " +
1015+
"an empty [" + DataTier.TIER_PREFERENCE + "] setting, in 8.0 this setting will be required for all indices");
1016+
}
1017+
10061018
return Optional.empty();
10071019
}
10081020
}

server/src/test/java/org/elasticsearch/cluster/routing/allocation/DataTierTests.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package org.elasticsearch.cluster.routing.allocation;
1010

1111
import org.elasticsearch.Version;
12+
import org.elasticsearch.cluster.ClusterState;
1213
import org.elasticsearch.cluster.node.DiscoveryNode;
1314
import org.elasticsearch.cluster.node.DiscoveryNodeRole;
1415
import org.elasticsearch.cluster.node.DiscoveryNodes;
@@ -21,20 +22,27 @@
2122

2223
import java.util.ArrayList;
2324
import java.util.Arrays;
25+
import java.util.Collection;
2426
import java.util.HashMap;
2527
import java.util.HashSet;
2628
import java.util.List;
2729
import java.util.Map;
2830
import java.util.Set;
2931
import java.util.concurrent.atomic.AtomicInteger;
32+
import java.util.stream.Collectors;
3033

34+
import static org.elasticsearch.cluster.routing.allocation.DataTier.ALL_DATA_TIERS;
3135
import static org.elasticsearch.cluster.routing.allocation.DataTier.DATA_COLD;
3236
import static org.elasticsearch.cluster.routing.allocation.DataTier.DATA_HOT;
3337
import static org.elasticsearch.cluster.routing.allocation.DataTier.DATA_WARM;
3438
import static org.elasticsearch.cluster.routing.allocation.DataTier.getPreferredTiersConfiguration;
3539
import static org.hamcrest.CoreMatchers.is;
3640
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
41+
import static org.hamcrest.Matchers.both;
42+
import static org.hamcrest.Matchers.contains;
3743
import static org.hamcrest.Matchers.hasItem;
44+
import static org.hamcrest.Matchers.hasProperty;
45+
import static org.hamcrest.Matchers.hasSize;
3846
import static org.hamcrest.Matchers.not;
3947

4048
public class DataTierTests extends ESTestCase {
@@ -139,6 +147,41 @@ public void testGetPreferredTiersConfiguration() {
139147
assertThat(exception.getMessage(), is("invalid data tier [no_tier]"));
140148
}
141149

150+
public void testDataNodesWithoutAllDataRoles() {
151+
ClusterState clusterState = clusterStateWithoutAllDataRoles();
152+
Set<DiscoveryNode> nodes = DataTier.dataNodesWithoutAllDataRoles(clusterState);
153+
assertThat(nodes, hasSize(2));
154+
assertThat(nodes, hasItem(both(hasProperty("name", is("name_3")))
155+
.and(hasProperty("roles", contains(DiscoveryNodeRole.DATA_FROZEN_NODE_ROLE)))));
156+
assertThat(nodes, hasItem(hasProperty("name", is("name_4"))));
157+
}
158+
159+
public static ClusterState clusterStateWithoutAllDataRoles() {
160+
Set<DiscoveryNodeRole> allDataRoles = new HashSet<>(DiscoveryNodeRole.BUILT_IN_ROLES).stream()
161+
.filter(role -> ALL_DATA_TIERS.contains(role.roleName())).collect(Collectors.toSet());
162+
163+
Collection<String> allButOneDataTiers = randomValueOtherThan(ALL_DATA_TIERS,
164+
() -> randomSubsetOf(randomIntBetween(1, ALL_DATA_TIERS.size() - 1), ALL_DATA_TIERS));
165+
Set<DiscoveryNodeRole> allButOneDataRoles = new HashSet<>(DiscoveryNodeRole.BUILT_IN_ROLES).stream()
166+
.filter(role -> allButOneDataTiers.contains(role.roleName())).collect(Collectors.toSet());
167+
168+
DiscoveryNodes.Builder discoBuilder = DiscoveryNodes.builder();
169+
List<DiscoveryNode> nodesList = org.elasticsearch.core.List.of(
170+
newNode(0, org.elasticsearch.core.Map.of(), org.elasticsearch.core.Set.of(DiscoveryNodeRole.MASTER_ROLE)),
171+
newNode(1, org.elasticsearch.core.Map.of(), org.elasticsearch.core.Set.of(DiscoveryNodeRole.DATA_ROLE)),
172+
newNode(2, org.elasticsearch.core.Map.of(), allDataRoles),
173+
newNode(3, org.elasticsearch.core.Map.of(), org.elasticsearch.core.Set.of(DiscoveryNodeRole.DATA_FROZEN_NODE_ROLE)),
174+
newNode(4, org.elasticsearch.core.Map.of(), allButOneDataRoles)
175+
);
176+
for (DiscoveryNode node : nodesList) {
177+
discoBuilder = discoBuilder.add(node);
178+
}
179+
discoBuilder.localNodeId(randomFrom(nodesList).getId());
180+
discoBuilder.masterNodeId(randomFrom(nodesList).getId());
181+
182+
return ClusterState.builder(ClusterState.EMPTY_STATE).nodes(discoBuilder.build()).build();
183+
}
184+
142185
private static DiscoveryNodes buildDiscoveryNodes() {
143186
int numNodes = randomIntBetween(3, 15);
144187
DiscoveryNodes.Builder discoBuilder = DiscoveryNodes.builder();

server/src/test/java/org/elasticsearch/cluster/routing/allocation/FailedNodeRoutingTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ public void testRandomClusterPromotesNewestReplica() throws InterruptedException
132132
String name = "index_" + randomAlphaOfLength(8).toLowerCase(Locale.ROOT);
133133
Settings.Builder settingsBuilder = Settings.builder()
134134
.put(SETTING_NUMBER_OF_SHARDS, randomIntBetween(1, 4))
135-
.put(SETTING_NUMBER_OF_REPLICAS, randomIntBetween(2, 4));
135+
.put(SETTING_NUMBER_OF_REPLICAS, randomIntBetween(2, 4))
136+
.put(DataTier.TIER_PREFERENCE, DataTier.DATA_CONTENT);
136137
CreateIndexRequest request = new CreateIndexRequest(name, settingsBuilder.build()).waitForActiveShards(ActiveShardCount.NONE);
137138
state = cluster.createIndex(state, request);
138139
assertTrue(state.metadata().hasIndex(name));

server/src/test/java/org/elasticsearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.elasticsearch.cluster.routing.RoutingTable;
3333
import org.elasticsearch.cluster.routing.ShardRouting;
3434
import org.elasticsearch.cluster.routing.ShardRoutingState;
35+
import org.elasticsearch.cluster.routing.allocation.DataTier;
3536
import org.elasticsearch.cluster.routing.allocation.FailedShard;
3637
import org.elasticsearch.cluster.service.ClusterService;
3738
import org.elasticsearch.common.UUIDs;
@@ -341,7 +342,8 @@ public ClusterState randomlyUpdateClusterState(ClusterState state,
341342
}
342343
String name = "index_" + randomAlphaOfLength(15).toLowerCase(Locale.ROOT);
343344
Settings.Builder settingsBuilder = Settings.builder()
344-
.put(SETTING_NUMBER_OF_SHARDS, randomIntBetween(1, 3));
345+
.put(SETTING_NUMBER_OF_SHARDS, randomIntBetween(1, 3))
346+
.put(DataTier.TIER_PREFERENCE, DataTier.DATA_CONTENT);
345347
if (randomBoolean()) {
346348
int min = randomInt(2);
347349
int max = min + randomInt(3);

x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@
1313
import org.elasticsearch.common.settings.Setting;
1414
import org.elasticsearch.common.settings.Settings;
1515
import org.elasticsearch.license.XPackLicenseState;
16-
import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
1716
import org.elasticsearch.transport.TransportService;
1817
import org.elasticsearch.xpack.core.XPackSettings;
18+
import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
1919

2020
import java.util.Arrays;
2121
import java.util.Collections;
2222
import java.util.List;
2323
import java.util.Objects;
24+
import java.util.function.BiFunction;
2425
import java.util.function.Function;
2526
import java.util.stream.Collectors;
2627
import java.util.stream.Stream;
@@ -135,24 +136,25 @@ private DeprecationChecks() {
135136
).collect(Collectors.toList());
136137
}
137138

138-
static List<Function<IndexMetadata, DeprecationIssue>> INDEX_SETTINGS_CHECKS =
139+
static List<BiFunction<ClusterState, IndexMetadata, DeprecationIssue>> INDEX_SETTINGS_CHECKS =
139140
Collections.unmodifiableList(Arrays.asList(
140-
IndexDeprecationChecks::oldIndicesCheck,
141-
IndexDeprecationChecks::tooManyFieldsCheck,
142-
IndexDeprecationChecks::chainedMultiFieldsCheck,
143-
IndexDeprecationChecks::deprecatedDateTimeFormat,
144-
IndexDeprecationChecks::translogRetentionSettingCheck,
145-
IndexDeprecationChecks::fieldNamesDisabledCheck,
146-
IndexDeprecationChecks::checkIndexDataPath,
147-
IndexDeprecationChecks::indexingSlowLogLevelSettingCheck,
148-
IndexDeprecationChecks::searchSlowLogLevelSettingCheck,
149-
IndexDeprecationChecks::storeTypeSettingCheck,
150-
IndexDeprecationChecks::checkIndexRoutingRequireSetting,
151-
IndexDeprecationChecks::checkIndexRoutingIncludeSetting,
152-
IndexDeprecationChecks::checkIndexRoutingExcludeSetting,
153-
IndexDeprecationChecks::checkIndexMatrixFiltersSetting,
154-
IndexDeprecationChecks::checkGeoShapeMappings,
155-
IndexDeprecationChecks::frozenIndexSettingCheck
141+
(clusterState, indexMetadata) -> IndexDeprecationChecks.oldIndicesCheck(indexMetadata),
142+
(clusterState, indexMetadata) -> IndexDeprecationChecks.tooManyFieldsCheck(indexMetadata),
143+
(clusterState, indexMetadata) -> IndexDeprecationChecks.chainedMultiFieldsCheck(indexMetadata),
144+
(clusterState, indexMetadata) -> IndexDeprecationChecks.deprecatedDateTimeFormat(indexMetadata),
145+
(clusterState, indexMetadata) -> IndexDeprecationChecks.translogRetentionSettingCheck(indexMetadata),
146+
(clusterState, indexMetadata) -> IndexDeprecationChecks.fieldNamesDisabledCheck(indexMetadata),
147+
(clusterState, indexMetadata) -> IndexDeprecationChecks.checkIndexDataPath(indexMetadata),
148+
(clusterState, indexMetadata) -> IndexDeprecationChecks.indexingSlowLogLevelSettingCheck(indexMetadata),
149+
(clusterState, indexMetadata) -> IndexDeprecationChecks.searchSlowLogLevelSettingCheck(indexMetadata),
150+
(clusterState, indexMetadata) -> IndexDeprecationChecks.storeTypeSettingCheck(indexMetadata),
151+
(clusterState, indexMetadata) -> IndexDeprecationChecks.checkIndexRoutingRequireSetting(indexMetadata),
152+
(clusterState, indexMetadata) -> IndexDeprecationChecks.checkIndexRoutingIncludeSetting(indexMetadata),
153+
(clusterState, indexMetadata) -> IndexDeprecationChecks.checkIndexRoutingExcludeSetting(indexMetadata),
154+
(clusterState, indexMetadata) -> IndexDeprecationChecks.checkIndexMatrixFiltersSetting(indexMetadata),
155+
(clusterState, indexMetadata) -> IndexDeprecationChecks.checkGeoShapeMappings(indexMetadata),
156+
(clusterState, indexMetadata) -> IndexDeprecationChecks.frozenIndexSettingCheck(indexMetadata),
157+
IndexDeprecationChecks::emptyDataTierPreferenceCheck
156158
));
157159

158160
/**

x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationInfoAction.java

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
import org.elasticsearch.common.regex.Regex;
2626
import org.elasticsearch.common.settings.Settings;
2727
import org.elasticsearch.common.util.set.Sets;
28+
import org.elasticsearch.rest.RestStatus;
2829
import org.elasticsearch.xcontent.ToXContentObject;
2930
import org.elasticsearch.xcontent.XContentBuilder;
30-
import org.elasticsearch.rest.RestStatus;
3131
import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
3232

3333
import java.io.IOException;
@@ -39,6 +39,7 @@
3939
import java.util.Map;
4040
import java.util.Objects;
4141
import java.util.Set;
42+
import java.util.function.BiFunction;
4243
import java.util.function.Function;
4344
import java.util.stream.Collectors;
4445

@@ -198,14 +199,16 @@ public int hashCode() {
198199
* @param clusterSettingsChecks The list of cluster-level checks
199200
* @return The list of deprecation issues found in the cluster
200201
*/
201-
public static DeprecationInfoAction.Response from(ClusterState state,
202-
IndexNameExpressionResolver indexNameExpressionResolver,
203-
Request request,
204-
NodesDeprecationCheckResponse nodeDeprecationResponse,
205-
List<Function<IndexMetadata, DeprecationIssue>> indexSettingsChecks,
206-
List<Function<ClusterState, DeprecationIssue>> clusterSettingsChecks,
207-
Map<String, List<DeprecationIssue>> pluginSettingIssues,
208-
List<String> skipTheseDeprecatedSettings) {
202+
public static DeprecationInfoAction.Response from(
203+
ClusterState state,
204+
IndexNameExpressionResolver indexNameExpressionResolver,
205+
Request request,
206+
NodesDeprecationCheckResponse nodeDeprecationResponse,
207+
List<BiFunction<ClusterState, IndexMetadata, DeprecationIssue>> indexSettingsChecks,
208+
List<Function<ClusterState, DeprecationIssue>> clusterSettingsChecks,
209+
Map<String, List<DeprecationIssue>> pluginSettingIssues,
210+
List<String> skipTheseDeprecatedSettings
211+
) {
209212
// Allow system index access here to prevent deprecation warnings when we call this API
210213
String[] concreteIndexNames = indexNameExpressionResolver.concreteIndexNamesWithSystemIndexAccess(state, request);
211214
ClusterState stateWithSkippedSettingsRemoved = removeSkippedSettings(state, concreteIndexNames, skipTheseDeprecatedSettings);
@@ -217,7 +220,7 @@ public static DeprecationInfoAction.Response from(ClusterState state,
217220
for (String concreteIndex : concreteIndexNames) {
218221
IndexMetadata indexMetadata = stateWithSkippedSettingsRemoved.getMetadata().index(concreteIndex);
219222
List<DeprecationIssue> singleIndexIssues = filterChecks(indexSettingsChecks,
220-
c -> c.apply(indexMetadata));
223+
c -> c.apply(state, indexMetadata));
221224
if (singleIndexIssues.size() > 0) {
222225
indexSettingsIssues.put(concreteIndex, singleIndexIssues);
223226
}

0 commit comments

Comments
 (0)