[7.x] Add index.routing.allocation.include._tier_preference setting (#62589) (#62667)

This commit adds the `index.routing.allocation.prefer._tier` setting to the
`DataTierAllocationDecider`. This special-purpose allocation setting lets a user specify a
preference-based list of tiers for an index to be assigned to. For example, if the setting were set
to:

```
"index.routing.allocation.prefer._tier": "data_hot,data_warm,data_content"
```

If the cluster contains any nodes with the `data_hot` role, the decider will only allow them to be
allocated on the `data_hot` node(s). If there are no `data_hot` nodes, but there are `data_warm` and
`data_content` nodes, then the index will be allowed to be allocated on `data_warm` nodes.

This allows us to specify an index's preference for tier(s) without causing the index to be
unassigned if no nodes of a preferred tier are available.

Subsequent work will change the ILM migration to make additional use of this setting.

Relates to #60848
This commit is contained in:
Lee Hinman 2020-09-18 15:41:36 -06:00 committed by GitHub
parent c2e73ba061
commit 4a08928c47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 435 additions and 36 deletions

View File

@ -396,7 +396,7 @@ Returns:
"index.creation_date": "1474389951325",
"index.uuid": "n6gzFZTgS664GUfx0Xrpjw",
"index.version.created": ...,
"index.routing.allocation.include._tier" : "data_content",
"index.routing.allocation.include._tier_preference" : "data_content",
"index.provided_name" : "my-index-000001"
}
}
@ -433,7 +433,7 @@ Returns:
"routing": {
"allocation": {
"include": {
"_tier": "data_content"
"_tier_preference": "data_content"
}
}
},

View File

@ -116,7 +116,7 @@ PUT /my-index-000001?master_timeout=1s&timeout=1s
{
"settings": {
"index.routing.allocation.include._name": "non_existent_node",
"index.routing.allocation.include._tier": null
"index.routing.allocation.include._tier_preference": null
}
}

View File

@ -250,7 +250,7 @@ public class RecoveryIT extends AbstractRollingTestCase {
// but the recovering copy will be seen as invalid and the cluster health won't return to GREEN
// before timing out
.put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms")
.put("index.routing.allocation.include._tier", "")
.put("index.routing.allocation.include._tier_preference", "")
.put(SETTING_ALLOCATION_MAX_RETRY.getKey(), "0"); // fail faster
createIndex(index, settings.build());
indexDocs(index, 0, 10);
@ -267,7 +267,7 @@ public class RecoveryIT extends AbstractRollingTestCase {
.put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0)
.put(INDEX_ROUTING_ALLOCATION_ENABLE_SETTING.getKey(), (String)null)
.put("index.routing.allocation.include._id", oldNode)
.putNull("index.routing.allocation.include._tier")
.putNull("index.routing.allocation.include._tier_preference")
);
ensureGreen(index); // wait for the primary to be assigned
ensureNoInitializingShards(); // wait for all other shard activity to finish
@ -290,7 +290,7 @@ public class RecoveryIT extends AbstractRollingTestCase {
updateIndexSettings(index, Settings.builder()
.put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 2)
.put("index.routing.allocation.include._id", (String)null)
.putNull("index.routing.allocation.include._tier")
.putNull("index.routing.allocation.include._tier_preference")
);
asyncIndexDocs(index, 60, 45).get();
ensureGreen(index);

View File

@ -181,7 +181,7 @@ public class DiscoveryNodeFilters {
}
}
}
} else if ("_tier".equals(attr)) {
} else if (attr != null && attr.startsWith("_tier")) {
// Always allow _tier as an attribute, will be handled elsewhere
return true;
} else {

View File

@ -50,7 +50,7 @@ public class PrimaryFollowerAllocationIT extends CcrIntegTestCase {
final PutFollowAction.Request putFollowRequest = putFollow(leaderIndex, followerIndex);
putFollowRequest.setSettings(Settings.builder()
.put("index.routing.allocation.include._name", String.join(",", dataOnlyNodes))
.putNull("index.routing.allocation.include._tier")
.putNull("index.routing.allocation.include._tier_preference")
.build());
putFollowRequest.waitForActiveShards(ActiveShardCount.ONE);
putFollowRequest.timeout(TimeValue.timeValueSeconds(2));
@ -84,7 +84,7 @@ public class PrimaryFollowerAllocationIT extends CcrIntegTestCase {
.put("index.routing.rebalance.enable", "none")
.put("index.routing.allocation.include._name",
Stream.concat(dataOnlyNodes.stream(), dataAndRemoteNodes.stream()).collect(Collectors.joining(",")))
.putNull("index.routing.allocation.include._tier")
.putNull("index.routing.allocation.include._tier_preference")
.build());
final PutFollowAction.Response response = followerClient().execute(PutFollowAction.INSTANCE, putFollowRequest).get();
assertTrue(response.isFollowIndexShardsAcked());
@ -108,8 +108,8 @@ public class PrimaryFollowerAllocationIT extends CcrIntegTestCase {
followerClient().admin().indices().prepareUpdateSettings(followerIndex)
.setMasterNodeTimeout(TimeValue.MAX_VALUE)
.setSettings(Settings.builder()
.put("index.routing.allocation.include._name", String.join(",", dataOnlyNodes))
.putNull("index.routing.allocation.include._tier"))
.putNull("index.routing.allocation.include._tier_preference")
.put("index.routing.allocation.include._name", String.join(",", dataOnlyNodes)))
.get();
assertBusy(() -> {
final ClusterState state = getFollowerCluster().client().admin().cluster().prepareState().get().getState();

View File

@ -41,7 +41,7 @@ public class DataTierIT extends ESIntegTestCase {
client().admin().indices().prepareCreate(index).setWaitForActiveShards(0).get();
Settings idxSettings = client().admin().indices().prepareGetIndex().addIndices(index).get().getSettings().get(index);
assertThat(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE_SETTING.get(idxSettings), equalTo(DataTier.DATA_CONTENT));
assertThat(DataTierAllocationDecider.INDEX_ROUTING_PREFER_SETTING.get(idxSettings), equalTo(DataTier.DATA_CONTENT));
// index should be red
assertThat(client().admin().cluster().prepareHealth(index).get().getIndices().get(index).getStatus(),
@ -51,7 +51,7 @@ public class DataTierIT extends ESIntegTestCase {
logger.info("--> starting content node");
startContentOnlyNode();
} else {
logger.info("--> starting hot node");
logger.info("--> starting data node");
startDataNode();
}
@ -65,7 +65,7 @@ public class DataTierIT extends ESIntegTestCase {
ensureGreen();
String setting = randomBoolean() ? DataTierAllocationDecider.INDEX_ROUTING_REQUIRE :
DataTierAllocationDecider.INDEX_ROUTING_INCLUDE;
DataTierAllocationDecider.INDEX_ROUTING_PREFER;
client().admin().indices().prepareCreate(index)
.setWaitForActiveShards(0)
@ -89,13 +89,13 @@ public class DataTierIT extends ESIntegTestCase {
client().admin().indices().prepareCreate(index)
.setWaitForActiveShards(0)
.setSettings(Settings.builder()
.putNull(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE))
.putNull(DataTierAllocationDecider.INDEX_ROUTING_PREFER))
.get();
Settings idxSettings = client().admin().indices().prepareGetIndex().addIndices(index).get().getSettings().get(index);
assertThat(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE_SETTING.get(idxSettings), equalTo(""));
assertThat(DataTierAllocationDecider.INDEX_ROUTING_PREFER_SETTING.get(idxSettings), equalTo(""));
// Even the key shouldn't exist if it has been nulled out
assertFalse(idxSettings.keySet().toString(), idxSettings.keySet().contains(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE));
assertFalse(idxSettings.keySet().toString(), idxSettings.keySet().contains(DataTierAllocationDecider.INDEX_ROUTING_PREFER));
// index should be yellow
logger.info("--> waiting for {} to be yellow", index);
@ -103,7 +103,7 @@ public class DataTierIT extends ESIntegTestCase {
client().admin().indices().prepareDelete(index).get();
// Now test it overriding the "require" setting, in which case the include should be skipped
// Now test it overriding the "require" setting, in which case the preference should be skipped
client().admin().indices().prepareCreate(index)
.setWaitForActiveShards(0)
.setSettings(Settings.builder()
@ -111,9 +111,9 @@ public class DataTierIT extends ESIntegTestCase {
.get();
idxSettings = client().admin().indices().prepareGetIndex().addIndices(index).get().getSettings().get(index);
assertThat(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE_SETTING.get(idxSettings), equalTo(""));
assertThat(DataTierAllocationDecider.INDEX_ROUTING_PREFER_SETTING.get(idxSettings), equalTo(""));
// The key should not be put in place since it was overridden
assertFalse(idxSettings.keySet().contains(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE));
assertFalse(idxSettings.keySet().contains(DataTierAllocationDecider.INDEX_ROUTING_PREFER));
assertThat(DataTierAllocationDecider.INDEX_ROUTING_REQUIRE_SETTING.get(idxSettings), equalTo(DataTier.DATA_COLD));
// index should be yellow
@ -134,7 +134,7 @@ public class DataTierIT extends ESIntegTestCase {
.setSettings(Settings.builder()
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 2)
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
.put(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE, "data_warm"))
.put(DataTierAllocationDecider.INDEX_ROUTING_PREFER, "data_warm"))
.get();
client().admin().indices().prepareAddBlock(IndexMetadata.APIBlock.READ_ONLY, index).get();
@ -150,7 +150,7 @@ public class DataTierIT extends ESIntegTestCase {
Settings idxSettings = client().admin().indices().prepareGetIndex().addIndices(index + "-shrunk")
.get().getSettings().get(index + "-shrunk");
// It should inherit the setting of its originator
assertThat(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE_SETTING.get(idxSettings), equalTo(DataTier.DATA_WARM));
assertThat(DataTierAllocationDecider.INDEX_ROUTING_PREFER_SETTING.get(idxSettings), equalTo(DataTier.DATA_WARM));
// Required or else the test cleanup fails because it can't delete the indices
client().admin().indices().prepareUpdateSettings(index, index + "-shrunk")
@ -172,7 +172,7 @@ public class DataTierIT extends ESIntegTestCase {
client().admin().indices().prepareCreate(index).setWaitForActiveShards(0).get();
Settings idxSettings = client().admin().indices().prepareGetIndex().addIndices(index).get().getSettings().get(index);
assertThat(idxSettings.keySet().contains(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE), equalTo(false));
assertThat(idxSettings.keySet().contains(DataTierAllocationDecider.INDEX_ROUTING_PREFER), equalTo(false));
// index should be yellow
ensureYellow(index);
@ -180,7 +180,7 @@ public class DataTierIT extends ESIntegTestCase {
client().admin().indices().prepareDelete(index).get();
t = new Template(Settings.builder()
.putNull(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE)
.putNull(DataTierAllocationDecider.INDEX_ROUTING_PREFER)
.build(), null, null);
ct = new ComposableIndexTemplate(Collections.singletonList(index), t, null, null, null, null, null);
client().execute(PutComposableIndexTemplateAction.INSTANCE,
@ -189,7 +189,7 @@ public class DataTierIT extends ESIntegTestCase {
client().admin().indices().prepareCreate(index).setWaitForActiveShards(0).get();
idxSettings = client().admin().indices().prepareGetIndex().addIndices(index).get().getSettings().get(index);
assertThat(idxSettings.keySet().contains(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE), equalTo(false));
assertThat(idxSettings.keySet().contains(DataTierAllocationDecider.INDEX_ROUTING_PREFER), equalTo(false));
ensureYellow(index);
}

View File

@ -6,9 +6,11 @@
package org.elasticsearch.xpack.cluster.routing.allocation;
import com.carrotsearch.hppc.cursors.ObjectCursor;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.node.DiscoveryNodeRole;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.routing.RoutingNode;
import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
@ -21,6 +23,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.core.DataTier;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@ -38,6 +41,7 @@ public class DataTierAllocationDecider extends AllocationDecider {
public static final String CLUSTER_ROUTING_EXCLUDE = "cluster.routing.allocation.exclude._tier";
public static final String INDEX_ROUTING_REQUIRE = "index.routing.allocation.require._tier";
public static final String INDEX_ROUTING_INCLUDE = "index.routing.allocation.include._tier";
public static final String INDEX_ROUTING_PREFER = "index.routing.allocation.include._tier_preference";
public static final String INDEX_ROUTING_EXCLUDE = "index.routing.allocation.exclude._tier";
public static final Setting<String> CLUSTER_ROUTING_REQUIRE_SETTING = Setting.simpleString(CLUSTER_ROUTING_REQUIRE,
@ -52,6 +56,8 @@ public class DataTierAllocationDecider extends AllocationDecider {
DataTierAllocationDecider::validateTierSetting, Setting.Property.Dynamic, Setting.Property.IndexScope);
public static final Setting<String> INDEX_ROUTING_EXCLUDE_SETTING = Setting.simpleString(INDEX_ROUTING_EXCLUDE,
DataTierAllocationDecider::validateTierSetting, Setting.Property.Dynamic, Setting.Property.IndexScope);
public static final Setting<String> INDEX_ROUTING_PREFER_SETTING = Setting.simpleString(INDEX_ROUTING_PREFER,
DataTierAllocationDecider::validateTierSetting, Setting.Property.Dynamic, Setting.Property.IndexScope);
private static void validateTierSetting(String setting) {
if (Strings.hasText(setting)) {
@ -101,7 +107,12 @@ public class DataTierAllocationDecider extends AllocationDecider {
return decision;
}
return allocation.decision(Decision.YES, NAME, "node passes include/exclude/require tier filters");
decision = shouldIndexPreferTier(indexMetadata, node, allocation);
if (decision != null) {
return decision;
}
return allocation.decision(Decision.YES, NAME, "node passes include/exclude/require/prefer tier filters");
}
private Decision shouldFilter(ShardRouting shardRouting, DiscoveryNode node, RoutingAllocation allocation) {
@ -115,7 +126,12 @@ public class DataTierAllocationDecider extends AllocationDecider {
return decision;
}
return allocation.decision(Decision.YES, NAME, "node passes include/exclude/require tier filters");
decision = shouldIndexPreferTier(allocation.metadata().getIndexSafe(shardRouting.index()), node, allocation);
if (decision != null) {
return decision;
}
return allocation.decision(Decision.YES, NAME, "node passes include/exclude/require/prefer tier filters");
}
private Decision shouldFilter(IndexMetadata indexMd, DiscoveryNode node, RoutingAllocation allocation) {
@ -129,7 +145,37 @@ public class DataTierAllocationDecider extends AllocationDecider {
return decision;
}
return allocation.decision(Decision.YES, NAME, "node passes include/exclude/require tier filters");
decision = shouldIndexPreferTier(indexMd, node, allocation);
if (decision != null) {
return decision;
}
return allocation.decision(Decision.YES, NAME, "node passes include/exclude/require/prefer tier filters");
}
private Decision shouldIndexPreferTier(IndexMetadata indexMetadata, DiscoveryNode node, RoutingAllocation allocation) {
Settings indexSettings = indexMetadata.getSettings();
String tierPreference = INDEX_ROUTING_PREFER_SETTING.get(indexSettings);
if (Strings.hasText(tierPreference)) {
Optional<String> tier = preferredAvailableTier(tierPreference, allocation.nodes());
if (tier.isPresent()) {
String tierName = tier.get();
// The OpType doesn't actually matter here, because we have
// selected only a single tier as our "preferred" tier
if (allocationAllowed(OpType.AND, tierName, node)) {
return allocation.decision(Decision.YES, NAME,
"index has a preference for tiers [%s] and node has tier [%s]", tierPreference, tierName);
} else {
return allocation.decision(Decision.NO, NAME,
"index has a preference for tiers [%s] and node does not meet the required [%s] tier", tierPreference, tierName);
}
} else {
return allocation.decision(Decision.NO, NAME, "index has a preference for tiers [%s], " +
"but no nodes for any of those tiers are available in the cluster", tierPreference);
}
}
return null;
}
private Decision shouldIndexFilter(IndexMetadata indexMd, DiscoveryNode node, RoutingAllocation allocation) {
@ -186,6 +232,31 @@ public class DataTierAllocationDecider extends AllocationDecider {
OR
}
/**
* Given a string of comma-separated prioritized tiers (highest priority
* first) and an allocation, find the highest priority tier for which nodes
* exist. If no nodes for any of the tiers are available, returns an empty
* {@code Optional<String>}.
*/
static Optional<String> preferredAvailableTier(String prioritizedTiers, DiscoveryNodes nodes) {
String[] tiers = Strings.tokenizeToStringArray(prioritizedTiers, ",");
return Arrays.stream(tiers).filter(tier -> tierNodesPresent(tier, nodes)).findFirst();
}
static boolean tierNodesPresent(String singleTier, DiscoveryNodes nodes) {
assert singleTier.equals(DiscoveryNodeRole.DATA_ROLE.roleName()) || DataTier.validTierName(singleTier) :
"tier " + singleTier + " is an invalid tier name";
for (ObjectCursor<DiscoveryNode> node : nodes.getNodes().values()) {
if (node.value.getRoles().stream()
.map(DiscoveryNodeRole::roleName)
.anyMatch(s -> s.equals(DiscoveryNodeRole.DATA_ROLE.roleName()) || s.equals(singleTier))) {
return true;
}
}
return false;
}
private static boolean allocationAllowed(OpType opType, String tierSetting, DiscoveryNode node) {
String[] values = Strings.tokenizeToStringArray(tierSetting, ",");
for (String value : values) {

View File

@ -156,9 +156,9 @@ public class DataTier {
@Override
public Settings getAdditionalIndexSettings(String indexName, boolean isDataStreamIndex, Settings indexSettings) {
Set<String> settings = indexSettings.keySet();
if (settings.contains(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE)) {
if (settings.contains(DataTierAllocationDecider.INDEX_ROUTING_PREFER)) {
// It's okay to put it, it will be removed or overridden by the template/request settings
return Settings.builder().put(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE, DATA_HOT).build();
return Settings.builder().put(DataTierAllocationDecider.INDEX_ROUTING_PREFER, DATA_HOT).build();
} else if (settings.stream().anyMatch(s -> s.startsWith(IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_PREFIX + ".")) ||
settings.stream().anyMatch(s -> s.startsWith(IndexMetadata.INDEX_ROUTING_EXCLUDE_GROUP_PREFIX + ".")) ||
settings.stream().anyMatch(s -> s.startsWith(IndexMetadata.INDEX_ROUTING_INCLUDE_GROUP_PREFIX + "."))) {
@ -170,9 +170,9 @@ public class DataTier {
// tier if the index is part of a data stream, the "content"
// tier if it is not.
if (isDataStreamIndex) {
return Settings.builder().put(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE, DATA_HOT).build();
return Settings.builder().put(DataTierAllocationDecider.INDEX_ROUTING_PREFER, DATA_HOT).build();
} else {
return Settings.builder().put(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE, DATA_CONTENT).build();
return Settings.builder().put(DataTierAllocationDecider.INDEX_ROUTING_PREFER, DATA_CONTENT).build();
}
}
}

View File

@ -417,6 +417,7 @@ public class XPackPlugin extends XPackClientPlugin implements ExtensiblePlugin,
settings.add(DataTierAllocationDecider.INDEX_ROUTING_REQUIRE_SETTING);
settings.add(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE_SETTING);
settings.add(DataTierAllocationDecider.INDEX_ROUTING_EXCLUDE_SETTING);
settings.add(DataTierAllocationDecider.INDEX_ROUTING_PREFER_SETTING);
return settings;
}

View File

@ -92,7 +92,7 @@ public class MigrateAction implements LifecycleAction {
Settings.Builder migrationSettings = Settings.builder();
String dataTierName = "data_" + phase;
assert DataTier.validTierName(dataTierName) : "invalid data tier name:" + dataTierName;
migrationSettings.put(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE, dataTierName);
migrationSettings.put(DataTierAllocationDecider.INDEX_ROUTING_PREFER, dataTierName);
UpdateSettingsStep updateMigrationSettingStep = new UpdateSettingsStep(migrationKey, migrationRoutedKey, client,
migrationSettings.build());
DataTierMigrationRoutedStep migrationRoutedStep = new DataTierMigrationRoutedStep(migrationRoutedKey, nextStepKey);

View File

@ -36,6 +36,7 @@ import org.elasticsearch.xpack.core.DataTier;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import static org.hamcrest.Matchers.containsString;
@ -47,6 +48,7 @@ public class DataTierAllocationDeciderTests extends ESAllocationTestCase {
private static final DiscoveryNode HOT_NODE = newNode("node-hot", Collections.singleton(DataTier.DATA_HOT_NODE_ROLE));
private static final DiscoveryNode WARM_NODE = newNode("node-warm", Collections.singleton(DataTier.DATA_WARM_NODE_ROLE));
private static final DiscoveryNode COLD_NODE = newNode("node-cold", Collections.singleton(DataTier.DATA_COLD_NODE_ROLE));
private static final DiscoveryNode CONTENT_NODE = newNode("node-content", Collections.singleton(DataTier.DATA_CONTENT_NODE_ROLE));
private static final DiscoveryNode DATA_NODE = newNode("node-data", Collections.singleton(DiscoveryNodeRole.DATA_ROLE));
private final ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, ALL_SETTINGS);
@ -274,6 +276,277 @@ public class DataTierAllocationDeciderTests extends ESAllocationTestCase {
}
}
public void testIndexPrefer() {
ClusterState state = ClusterState.builder(service.reroute(ClusterState.EMPTY_STATE, "initial state"))
.nodes(DiscoveryNodes.builder()
.add(HOT_NODE)
.build())
.metadata(Metadata.builder()
.put(IndexMetadata.builder("myindex")
.settings(Settings.builder()
.put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetadata.SETTING_INDEX_UUID, "myindex")
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
.put(DataTierAllocationDecider.INDEX_ROUTING_PREFER, "data_warm,data_cold")
.build()))
.build())
.build();
RoutingAllocation allocation = new RoutingAllocation(allocationDeciders, state.getRoutingNodes(), state,
null, 0);
allocation.debugDecision(true);
Decision d;
RoutingNode node;
for (DiscoveryNode n : Arrays.asList(HOT_NODE, WARM_NODE, COLD_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold], " +
"but no nodes for any of those tiers are available in the cluster"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold], " +
"but no nodes for any of those tiers are available in the cluster"));
}
state = ClusterState.builder(service.reroute(ClusterState.EMPTY_STATE, "initial state"))
.nodes(DiscoveryNodes.builder()
.add(HOT_NODE)
.add(COLD_NODE)
.build())
.metadata(Metadata.builder()
.put(IndexMetadata.builder("myindex")
.settings(Settings.builder()
.put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetadata.SETTING_INDEX_UUID, "myindex")
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
.put(DataTierAllocationDecider.INDEX_ROUTING_PREFER, "data_warm,data_cold")
.build()))
.build())
.build();
allocation = new RoutingAllocation(allocationDeciders, state.getRoutingNodes(), state, null, 0);
allocation.debugDecision(true);
for (DiscoveryNode n : Arrays.asList(HOT_NODE, WARM_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] " +
"and node does not meet the required [data_cold] tier"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] " +
"and node does not meet the required [data_cold] tier"));
}
for (DiscoveryNode n : Arrays.asList(COLD_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.YES));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] and node has tier [data_cold]"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.YES));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] and node has tier [data_cold]"));
}
}
public void testIndexPreferWithInclude() {
ClusterState state = ClusterState.builder(service.reroute(ClusterState.EMPTY_STATE, "initial state"))
.nodes(DiscoveryNodes.builder()
.add(WARM_NODE)
.add(COLD_NODE)
.build())
.metadata(Metadata.builder()
.put(IndexMetadata.builder("myindex")
.settings(Settings.builder()
.put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetadata.SETTING_INDEX_UUID, "myindex")
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
.put(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE, "data_cold")
.put(DataTierAllocationDecider.INDEX_ROUTING_PREFER, "data_warm,data_cold")
.build()))
.build())
.build();
RoutingAllocation allocation = new RoutingAllocation(allocationDeciders, state.getRoutingNodes(), state,
null, 0);
allocation.debugDecision(true);
Decision d;
RoutingNode node;
for (DiscoveryNode n : Arrays.asList(HOT_NODE, WARM_NODE, CONTENT_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("node does not match any index setting [index.routing.allocation.include._tier] tier filters [data_cold]"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("node does not match any index setting [index.routing.allocation.include._tier] tier filters [data_cold]"));
}
for (DiscoveryNode n : Arrays.asList(COLD_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] " +
"and node does not meet the required [data_warm] tier"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] " +
"and node does not meet the required [data_warm] tier"));
}
for (DiscoveryNode n : Arrays.asList(DATA_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.YES));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] and node has tier [data_warm]"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.YES));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] and node has tier [data_warm]"));
}
}
public void testIndexPreferWithExclude() {
ClusterState state = ClusterState.builder(service.reroute(ClusterState.EMPTY_STATE, "initial state"))
.nodes(DiscoveryNodes.builder()
.add(WARM_NODE)
.add(COLD_NODE)
.build())
.metadata(Metadata.builder()
.put(IndexMetadata.builder("myindex")
.settings(Settings.builder()
.put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetadata.SETTING_INDEX_UUID, "myindex")
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
.put(DataTierAllocationDecider.INDEX_ROUTING_EXCLUDE, "data_warm")
.put(DataTierAllocationDecider.INDEX_ROUTING_PREFER, "data_warm,data_cold")
.build()))
.build())
.build();
RoutingAllocation allocation = new RoutingAllocation(allocationDeciders, state.getRoutingNodes(), state,
null, 0);
allocation.debugDecision(true);
Decision d;
RoutingNode node;
for (DiscoveryNode n : Arrays.asList(HOT_NODE, COLD_NODE, CONTENT_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] " +
"and node does not meet the required [data_warm] tier"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] " +
"and node does not meet the required [data_warm] tier"));
}
for (DiscoveryNode n : Arrays.asList(WARM_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("node matches any index setting [index.routing.allocation.exclude._tier] tier filters [data_warm]"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("node matches any index setting [index.routing.allocation.exclude._tier] tier filters [data_warm]"));
}
for (DiscoveryNode n : Arrays.asList(DATA_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("node matches any index setting [index.routing.allocation.exclude._tier] tier filters [data_warm]"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("node matches any index setting [index.routing.allocation.exclude._tier] tier filters [data_warm]"));
}
}
public void testIndexPreferWithRequire() {
ClusterState state = ClusterState.builder(service.reroute(ClusterState.EMPTY_STATE, "initial state"))
.nodes(DiscoveryNodes.builder()
.add(WARM_NODE)
.add(COLD_NODE)
.build())
.metadata(Metadata.builder()
.put(IndexMetadata.builder("myindex")
.settings(Settings.builder()
.put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetadata.SETTING_INDEX_UUID, "myindex")
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
.put(DataTierAllocationDecider.INDEX_ROUTING_REQUIRE, "data_cold")
.put(DataTierAllocationDecider.INDEX_ROUTING_PREFER, "data_warm,data_cold")
.build()))
.build())
.build();
RoutingAllocation allocation = new RoutingAllocation(allocationDeciders, state.getRoutingNodes(), state,
null, 0);
allocation.debugDecision(true);
Decision d;
RoutingNode node;
for (DiscoveryNode n : Arrays.asList(HOT_NODE, WARM_NODE, CONTENT_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("node does not match all index setting [index.routing.allocation.require._tier] tier filters [data_cold]"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("node does not match all index setting [index.routing.allocation.require._tier] tier filters [data_cold]"));
}
for (DiscoveryNode n : Arrays.asList(COLD_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] " +
"and node does not meet the required [data_warm] tier"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.NO));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] " +
"and node does not meet the required [data_warm] tier"));
}
for (DiscoveryNode n : Arrays.asList(DATA_NODE)) {
node = new RoutingNode(n.getId(), n, shard);
d = decider.canAllocate(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.YES));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] and node has tier [data_warm]"));
d = decider.canRemain(shard, node, allocation);
assertThat(node.toString(), d.type(), equalTo(Decision.Type.YES));
assertThat(node.toString(), d.getExplanation(),
containsString("index has a preference for tiers [data_warm,data_cold] and node has tier [data_warm]"));
}
}
public void testClusterAndIndex() {
ClusterState state = prepareState(service.reroute(ClusterState.EMPTY_STATE, "initial state"),
Settings.builder()
@ -323,6 +596,60 @@ public class DataTierAllocationDeciderTests extends ESAllocationTestCase {
}
}
public void testTierNodesPresent() {
DiscoveryNodes nodes = DiscoveryNodes.builder().build();
assertFalse(DataTierAllocationDecider.tierNodesPresent("data", nodes));
assertFalse(DataTierAllocationDecider.tierNodesPresent("data_hot", nodes));
assertFalse(DataTierAllocationDecider.tierNodesPresent("data_warm", nodes));
assertFalse(DataTierAllocationDecider.tierNodesPresent("data_cold", nodes));
assertFalse(DataTierAllocationDecider.tierNodesPresent("data_content", nodes));
nodes = DiscoveryNodes.builder()
.add(WARM_NODE)
.add(CONTENT_NODE)
.build();
assertFalse(DataTierAllocationDecider.tierNodesPresent("data", nodes));
assertFalse(DataTierAllocationDecider.tierNodesPresent("data_hot", nodes));
assertTrue(DataTierAllocationDecider.tierNodesPresent("data_warm", nodes));
assertFalse(DataTierAllocationDecider.tierNodesPresent("data_cold", nodes));
assertTrue(DataTierAllocationDecider.tierNodesPresent("data_content", nodes));
nodes = DiscoveryNodes.builder()
.add(DATA_NODE)
.build();
assertTrue(DataTierAllocationDecider.tierNodesPresent("data", nodes));
assertTrue(DataTierAllocationDecider.tierNodesPresent("data_hot", nodes));
assertTrue(DataTierAllocationDecider.tierNodesPresent("data_warm", nodes));
assertTrue(DataTierAllocationDecider.tierNodesPresent("data_cold", nodes));
assertTrue(DataTierAllocationDecider.tierNodesPresent("data_content", nodes));
}
public void testPreferredTierAvailable() {
DiscoveryNodes nodes = DiscoveryNodes.builder().build();
assertThat(DataTierAllocationDecider.preferredAvailableTier("data", nodes), equalTo(Optional.empty()));
assertThat(DataTierAllocationDecider.preferredAvailableTier("data_hot,data_warm", nodes), equalTo(Optional.empty()));
assertThat(DataTierAllocationDecider.preferredAvailableTier("data_warm,data_content", nodes), equalTo(Optional.empty()));
assertThat(DataTierAllocationDecider.preferredAvailableTier("data_cold", nodes), equalTo(Optional.empty()));
nodes = DiscoveryNodes.builder()
.add(WARM_NODE)
.add(CONTENT_NODE)
.build();
assertThat(DataTierAllocationDecider.preferredAvailableTier("data", nodes), equalTo(Optional.empty()));
assertThat(DataTierAllocationDecider.preferredAvailableTier("data_hot,data_warm", nodes), equalTo(Optional.of("data_warm")));
assertThat(DataTierAllocationDecider.preferredAvailableTier("data_warm,data_content", nodes), equalTo(Optional.of("data_warm")));
assertThat(DataTierAllocationDecider.preferredAvailableTier("data_content,data_warm", nodes), equalTo(Optional.of("data_content")));
assertThat(DataTierAllocationDecider.preferredAvailableTier("data_hot,data_content,data_warm", nodes),
equalTo(Optional.of("data_content")));
assertThat(DataTierAllocationDecider.preferredAvailableTier("data_hot,data_cold,data_warm", nodes),
equalTo(Optional.of("data_warm")));
}
private ClusterState prepareState(ClusterState initialState) {
return prepareState(initialState, Settings.EMPTY);
}

View File

@ -64,7 +64,7 @@ public class DataTierDataStreamIT extends ESIntegTestCase {
.get()
.getSettings()
.get(DataStream.getDefaultBackingIndexName(index, 1));
assertThat(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE_SETTING.get(idxSettings), equalTo(DataTier.DATA_HOT));
assertThat(DataTierAllocationDecider.INDEX_ROUTING_PREFER_SETTING.get(idxSettings), equalTo(DataTier.DATA_HOT));
logger.info("--> waiting for {} to be yellow", index);
ensureYellow(index);
@ -78,7 +78,7 @@ public class DataTierDataStreamIT extends ESIntegTestCase {
.get()
.getSettings()
.get(DataStream.getDefaultBackingIndexName(index, 2));
assertThat(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE_SETTING.get(idxSettings), equalTo(DataTier.DATA_HOT));
assertThat(DataTierAllocationDecider.INDEX_ROUTING_PREFER_SETTING.get(idxSettings), equalTo(DataTier.DATA_HOT));
client().execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request(new String[] { index }));
}

View File

@ -657,7 +657,7 @@ public class TimeSeriesLifecycleActionsIT extends ESRestTestCase {
createIndexWithSettings(client(), index, alias, Settings.builder()
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numShards)
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
.putNull(DataTierAllocationDecider.INDEX_ROUTING_INCLUDE));
.putNull(DataTierAllocationDecider.INDEX_ROUTING_PREFER));
ensureGreen(index);