Skip to content

Commit

Permalink
[7.10] Make FilterAllocationDecider totally ignore tier-based allocat…
Browse files Browse the repository at this point in the history
…ion settings (#67019) (#67034)

Previously we treated attribute filtering for _tier-prefixed attributes a pass-through, meaning
that they were essentially always treated as matching in DiscoveryNodeFilters.match, however, for
exclude settings, this meant that the node was considered to match the node if a _tier* filter was
specified.

This commit prunes these attributes from the DiscoveryNodeFilters when considering the filters for
FilterAllocationDecider so that they are only considered in DataTierAllocationDecider.

Resolves #66679
  • Loading branch information
dakrone committed Jan 5, 2021
1 parent 8e294d5 commit e9b798b
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

public class DiscoveryNodeFilters {

Expand Down Expand Up @@ -88,6 +89,32 @@ private boolean matchByIP(String[] values, @Nullable String hostIp, @Nullable St
return false;
}

/**
* Removes any filters that should not be considered, returning a new
* {@link DiscoveryNodeFilters} object. If the filtered object has no
* filters after trimming, {@code null} is returned.
*/
@Nullable
public static DiscoveryNodeFilters trimTier(@Nullable DiscoveryNodeFilters original) {
if (original == null) {
return null;
}

Map<String, String[]> newFilters = original.filters.entrySet().stream()
// Remove all entries that start with "_tier", as these will be handled elsewhere
.filter(entry -> {
String attr = entry.getKey();
return attr != null && attr.startsWith("_tier") == false;
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

if (newFilters.size() == 0) {
return null;
} else {
return new DiscoveryNodeFilters(original.opType, newFilters);
}
}

public boolean match(DiscoveryNode node) {
for (Map.Entry<String, String[]> entry : filters.entrySet()) {
String attr = entry.getKey();
Expand Down Expand Up @@ -181,9 +208,6 @@ public boolean match(DiscoveryNode node) {
}
}
}
} else if (attr != null && attr.startsWith("_tier")) {
// Always allow _tier as an attribute, will be handled elsewhere
return true;
} else {
String nodeAttributeValue = node.getAttributes().get(attr);
if (nodeAttributeValue == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public Decision canAllocate(ShardRouting shardRouting, RoutingNode node, Routing
// that once it has been allocated post API the replicas can be allocated elsewhere without user interaction
// this is a setting that can only be set within the system!
IndexMetadata indexMd = allocation.metadata().getIndexSafe(shardRouting.index());
DiscoveryNodeFilters initialRecoveryFilters = indexMd.getInitialRecoveryFilters();
DiscoveryNodeFilters initialRecoveryFilters = DiscoveryNodeFilters.trimTier(indexMd.getInitialRecoveryFilters());
if (initialRecoveryFilters != null &&
shardRouting.recoverySource().getType() == RecoverySource.Type.LOCAL_SHARDS &&
initialRecoveryFilters.match(node.node()) == false) {
Expand Down Expand Up @@ -155,22 +155,26 @@ private Decision shouldFilter(IndexMetadata indexMd, DiscoveryNode node, Routing
}

private Decision shouldIndexFilter(IndexMetadata indexMd, DiscoveryNode node, RoutingAllocation allocation) {
if (indexMd.requireFilters() != null) {
if (indexMd.requireFilters().match(node) == false) {
DiscoveryNodeFilters indexRequireFilters = DiscoveryNodeFilters.trimTier(indexMd.requireFilters());
DiscoveryNodeFilters indexIncludeFilters = DiscoveryNodeFilters.trimTier(indexMd.includeFilters());
DiscoveryNodeFilters indexExcludeFilters = DiscoveryNodeFilters.trimTier(indexMd.excludeFilters());

if (indexRequireFilters != null) {
if (indexRequireFilters.match(node) == false) {
return allocation.decision(Decision.NO, NAME, "node does not match index setting [%s] filters [%s]",
IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_PREFIX, indexMd.requireFilters());
IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_PREFIX, indexRequireFilters);
}
}
if (indexMd.includeFilters() != null) {
if (indexMd.includeFilters().match(node) == false) {
if (indexIncludeFilters != null) {
if (indexIncludeFilters.match(node) == false) {
return allocation.decision(Decision.NO, NAME, "node does not match index setting [%s] filters [%s]",
IndexMetadata.INDEX_ROUTING_INCLUDE_GROUP_PREFIX, indexMd.includeFilters());
IndexMetadata.INDEX_ROUTING_INCLUDE_GROUP_PREFIX, indexIncludeFilters);
}
}
if (indexMd.excludeFilters() != null) {
if (indexMd.excludeFilters().match(node)) {
if (indexExcludeFilters != null) {
if (indexExcludeFilters.match(node)) {
return allocation.decision(Decision.NO, NAME, "node matches index setting [%s] filters [%s]",
IndexMetadata.INDEX_ROUTING_EXCLUDE_GROUP_SETTING.getKey(), indexMd.excludeFilters());
IndexMetadata.INDEX_ROUTING_EXCLUDE_GROUP_SETTING.getKey(), indexExcludeFilters);
}
}
return null;
Expand Down Expand Up @@ -199,12 +203,12 @@ private Decision shouldClusterFilter(DiscoveryNode node, RoutingAllocation alloc
}

private void setClusterRequireFilters(Map<String, String> filters) {
clusterRequireFilters = DiscoveryNodeFilters.buildFromKeyValue(AND, filters);
clusterRequireFilters = DiscoveryNodeFilters.trimTier(DiscoveryNodeFilters.buildFromKeyValue(AND, filters));
}
private void setClusterIncludeFilters(Map<String, String> filters) {
clusterIncludeFilters = DiscoveryNodeFilters.buildFromKeyValue(OR, filters);
clusterIncludeFilters = DiscoveryNodeFilters.trimTier(DiscoveryNodeFilters.buildFromKeyValue(OR, filters));
}
private void setClusterExcludeFilters(Map<String, String> filters) {
clusterExcludeFilters = DiscoveryNodeFilters.buildFromKeyValue(OR, filters);
clusterExcludeFilters = DiscoveryNodeFilters.trimTier(DiscoveryNodeFilters.buildFromKeyValue(OR, filters));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,54 @@ public void testFilterInitialRecovery() {
assertEquals("node passes include/exclude/require filters", decision.getExplanation());
}

private ClusterState createInitialClusterState(AllocationService service, Settings settings) {
public void testTierFilterIgnored() {
ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
FilterAllocationDecider filterAllocationDecider = new FilterAllocationDecider(Settings.EMPTY, clusterSettings);
AllocationDeciders allocationDeciders = new AllocationDeciders(
Arrays.asList(filterAllocationDecider,
new SameShardAllocationDecider(Settings.EMPTY, clusterSettings),
new ReplicaAfterPrimaryActiveAllocationDecider()));
AllocationService service = new AllocationService(allocationDeciders,
new TestGatewayAllocator(), new BalancedShardsAllocator(Settings.EMPTY), EmptyClusterInfoService.INSTANCE,
EmptySnapshotsInfoService.INSTANCE);
ClusterState state = createInitialClusterState(service, Settings.builder()
.put("index.routing.allocation.require._tier", "data_cold")
.put("index.routing.allocation.include._tier", "data_cold")
.put("index.routing.allocation.include._tier_preference", "data_cold")
.put("index.routing.allocation.exclude._tier", "data_cold")
.build(),
Settings.builder()
.put("cluster.routing.allocation.require._tier", "data_cold")
.put("cluster.routing.allocation.include._tier", "data_cold")
.put("cluster.routing.allocation.exclude._tier", "data_cold")
.build());
RoutingTable routingTable = state.routingTable();
RoutingAllocation allocation = new RoutingAllocation(allocationDeciders, state.getRoutingNodes(), state,
null, null, 0);
allocation.debugDecision(true);
allocation = new RoutingAllocation(allocationDeciders, state.getRoutingNodes(), state,
null, null, 0);
allocation.debugDecision(true);
Decision.Single decision = (Decision.Single) filterAllocationDecider.canAllocate(
routingTable.index("idx").shard(0).shards().get(0),
state.getRoutingNodes().node("node2"), allocation);
assertEquals(decision.toString(), Type.YES, decision.type());
assertEquals("node passes include/exclude/require filters", decision.getExplanation());
decision = (Decision.Single) filterAllocationDecider.canAllocate(
routingTable.index("idx").shard(0).shards().get(0),
state.getRoutingNodes().node("node1"), allocation);
assertEquals(Type.YES, decision.type());
assertEquals("node passes include/exclude/require filters", decision.getExplanation());
}

private ClusterState createInitialClusterState(AllocationService service, Settings indexSettings) {
return createInitialClusterState(service, indexSettings, Settings.EMPTY);
}

private ClusterState createInitialClusterState(AllocationService service, Settings idxSettings, Settings clusterSettings) {
Metadata.Builder metadata = Metadata.builder();
final Settings.Builder indexSettings = settings(Version.CURRENT).put(settings);
metadata.persistentSettings(clusterSettings);
final Settings.Builder indexSettings = settings(Version.CURRENT).put(idxSettings);
final IndexMetadata sourceIndex;
//put a fake closed source index
sourceIndex = IndexMetadata.builder("sourceIndex")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,26 @@ public void testDataTierTelemetry() {
assertThat(usage.getTierStats().get(DataTier.DATA_HOT).primaryShardBytesMAD, greaterThanOrEqualTo(0L));
}

public void testTierFilteringIgnoredByFilterAllocationDecider() {
startContentOnlyNode();
startHotOnlyNode();

// Exclude all data_cold nodes
client().admin().cluster().prepareUpdateSettings()
.setTransientSettings(Settings.builder()
.put(DataTierAllocationDecider.CLUSTER_ROUTING_EXCLUDE, "data_cold")
.build())
.get();

// Create an index, which should be excluded just fine, ignored by the FilterAllocationDecider
client().admin().indices().prepareCreate(index)
.setSettings(Settings.builder()
.put("index.number_of_shards", 2)
.put("index.number_of_replicas", 0))
.setWaitForActiveShards(0)
.get();
}

private DataTiersFeatureSetUsage getUsage() {
XPackUsageResponse usages = new XPackUsageRequestBuilder(client()).execute().actionGet();
XPackFeatureSet.Usage dtUsage = usages.getUsages().stream()
Expand Down

0 comments on commit e9b798b

Please sign in to comment.