|
@@ -20,6 +20,7 @@ package org.apache.hadoop.hdds.scm.net;
|
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
|
import com.google.common.base.Preconditions;
|
|
|
import com.google.common.collect.Lists;
|
|
|
+import org.apache.commons.collections.CollectionUtils;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
@@ -283,7 +284,9 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
scope = ROOT;
|
|
|
}
|
|
|
if (scope.startsWith(SCOPE_REVERSE_STR)) {
|
|
|
- return chooseRandom(ROOT, scope.substring(1), null, null,
|
|
|
+ ArrayList<String> excludedScopes = new ArrayList();
|
|
|
+ excludedScopes.add(scope.substring(1));
|
|
|
+ return chooseRandom(ROOT, excludedScopes, null, null,
|
|
|
ANCESTOR_GENERATION_DEFAULT);
|
|
|
} else {
|
|
|
return chooseRandom(scope, null, null, null, ANCESTOR_GENERATION_DEFAULT);
|
|
@@ -294,12 +297,12 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
* Randomly choose a node in the scope, ano not in the exclude scope.
|
|
|
* @param scope range of nodes from which a node will be chosen. cannot start
|
|
|
* with ~
|
|
|
- * @param excludedScope the chosen node cannot be in this range. cannot
|
|
|
+ * @param excludedScopes the chosen node cannot be in these ranges. cannot
|
|
|
* starts with ~
|
|
|
* @return the chosen node
|
|
|
*/
|
|
|
- public Node chooseRandom(String scope, String excludedScope) {
|
|
|
- return chooseRandom(scope, excludedScope, null, null,
|
|
|
+ public Node chooseRandom(String scope, List<String> excludedScopes) {
|
|
|
+ return chooseRandom(scope, excludedScopes, null, null,
|
|
|
ANCESTOR_GENERATION_DEFAULT);
|
|
|
}
|
|
|
|
|
@@ -320,7 +323,9 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
scope = ROOT;
|
|
|
}
|
|
|
if (scope.startsWith(SCOPE_REVERSE_STR)) {
|
|
|
- return chooseRandom(ROOT, scope.substring(1), excludedNodes, null,
|
|
|
+ ArrayList<String> excludedScopes = new ArrayList();
|
|
|
+ excludedScopes.add(scope.substring(1));
|
|
|
+ return chooseRandom(ROOT, excludedScopes, excludedNodes, null,
|
|
|
ANCESTOR_GENERATION_DEFAULT);
|
|
|
} else {
|
|
|
return chooseRandom(scope, null, excludedNodes, null,
|
|
@@ -352,33 +357,15 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
scope = ROOT;
|
|
|
}
|
|
|
if (scope.startsWith(SCOPE_REVERSE_STR)) {
|
|
|
- return chooseRandom(ROOT, scope.substring(1), excludedNodes, null,
|
|
|
+ ArrayList<String> excludedScopes = new ArrayList();
|
|
|
+ excludedScopes.add(scope.substring(1));
|
|
|
+ return chooseRandom(ROOT, excludedScopes, excludedNodes, null,
|
|
|
ancestorGen);
|
|
|
} else {
|
|
|
return chooseRandom(scope, null, excludedNodes, null, ancestorGen);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Randomly choose a leaf node.
|
|
|
- *
|
|
|
- * @param scope range from which a node will be chosen, cannot start with ~
|
|
|
- * @param excludedNodes nodes to be excluded
|
|
|
- * @param excludedScope excluded node range. Cannot start with ~
|
|
|
- * @param ancestorGen matters when excludeNodes is not null. It means the
|
|
|
- * ancestor generation that's not allowed to share between chosen node and the
|
|
|
- * excludedNodes. For example, if ancestorGen is 1, means chosen node
|
|
|
- * cannot share the same parent with excludeNodes. If value is 2, cannot
|
|
|
- * share the same grand parent, and so on. If ancestorGen is 0, then no
|
|
|
- * effect.
|
|
|
- *
|
|
|
- * @return the chosen node
|
|
|
- */
|
|
|
- public Node chooseRandom(String scope, String excludedScope,
|
|
|
- Collection<Node> excludedNodes, int ancestorGen) {
|
|
|
- return chooseRandom(scope, excludedScope, excludedNodes, null, ancestorGen);
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Randomly choose one leaf node from <i>scope</i>, share the same generation
|
|
|
* ancestor with <i>affinityNode</i>, and exclude nodes in
|
|
@@ -386,7 +373,7 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
*
|
|
|
* @param scope range of nodes from which a node will be chosen, cannot start
|
|
|
* with ~
|
|
|
- * @param excludedScope range of nodes to be excluded, cannot start with ~
|
|
|
+ * @param excludedScopes ranges of nodes to be excluded, cannot start with ~
|
|
|
* @param excludedNodes nodes to be excluded
|
|
|
* @param affinityNode when not null, the chosen node should share the same
|
|
|
* ancestor with this node at generation ancestorGen.
|
|
@@ -397,20 +384,20 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
* excludedNodes if affinityNode is null
|
|
|
* @return the chosen node
|
|
|
*/
|
|
|
- public Node chooseRandom(String scope, String excludedScope,
|
|
|
+ public Node chooseRandom(String scope, List<String> excludedScopes,
|
|
|
Collection<Node> excludedNodes, Node affinityNode, int ancestorGen) {
|
|
|
if (scope == null) {
|
|
|
scope = ROOT;
|
|
|
}
|
|
|
|
|
|
checkScope(scope);
|
|
|
- checkExcludedScope(excludedScope);
|
|
|
+ checkExcludedScopes(excludedScopes);
|
|
|
checkAffinityNode(affinityNode);
|
|
|
checkAncestorGen(ancestorGen);
|
|
|
|
|
|
netlock.readLock().lock();
|
|
|
try {
|
|
|
- return chooseNodeInternal(scope, -1, excludedScope,
|
|
|
+ return chooseNodeInternal(scope, -1, excludedScopes,
|
|
|
excludedNodes, affinityNode, ancestorGen);
|
|
|
} finally {
|
|
|
netlock.readLock().unlock();
|
|
@@ -426,7 +413,7 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
* excludedNodes
|
|
|
* @param scope range of nodes from which a node will be chosen, cannot start
|
|
|
* with ~
|
|
|
- * @param excludedScope range of nodes to be excluded, cannot start with ~
|
|
|
+ * @param excludedScopes ranges of nodes to be excluded, cannot start with ~
|
|
|
* @param excludedNodes nodes to be excluded
|
|
|
* @param affinityNode when not null, the chosen node should share the same
|
|
|
* ancestor with this node at generation ancestorGen.
|
|
@@ -466,20 +453,20 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
* from subtree /dc1. LeafIndex 1, so we pick the 2nd available node n4.
|
|
|
*
|
|
|
*/
|
|
|
- public Node getNode(int leafIndex, String scope, String excludedScope,
|
|
|
+ public Node getNode(int leafIndex, String scope, List<String> excludedScopes,
|
|
|
Collection<Node> excludedNodes, Node affinityNode, int ancestorGen) {
|
|
|
Preconditions.checkArgument(leafIndex >= 0);
|
|
|
if (scope == null) {
|
|
|
scope = ROOT;
|
|
|
}
|
|
|
checkScope(scope);
|
|
|
- checkExcludedScope(excludedScope);
|
|
|
+ checkExcludedScopes(excludedScopes);
|
|
|
checkAffinityNode(affinityNode);
|
|
|
checkAncestorGen(ancestorGen);
|
|
|
|
|
|
netlock.readLock().lock();
|
|
|
try {
|
|
|
- return chooseNodeInternal(scope, leafIndex, excludedScope,
|
|
|
+ return chooseNodeInternal(scope, leafIndex, excludedScopes,
|
|
|
excludedNodes, affinityNode, ancestorGen);
|
|
|
} finally {
|
|
|
netlock.readLock().unlock();
|
|
@@ -487,8 +474,8 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
}
|
|
|
|
|
|
private Node chooseNodeInternal(String scope, int leafIndex,
|
|
|
- String excludedScope, Collection<Node> excludedNodes, Node affinityNode,
|
|
|
- int ancestorGen) {
|
|
|
+ List<String> excludedScopes, Collection<Node> excludedNodes,
|
|
|
+ Node affinityNode, int ancestorGen) {
|
|
|
Preconditions.checkArgument(scope != null);
|
|
|
|
|
|
String finalScope = scope;
|
|
@@ -509,40 +496,48 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
ancestorGen = 0;
|
|
|
}
|
|
|
|
|
|
- // check overlap of excludedScope and finalScope
|
|
|
- if (excludedScope != null) {
|
|
|
- // excludeScope covers finalScope
|
|
|
- if (finalScope.startsWith(excludedScope)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- // excludeScope and finalScope share nothing
|
|
|
- if (!excludedScope.startsWith(finalScope)) {
|
|
|
- excludedScope = null;
|
|
|
+ // check overlap of excludedScopes and finalScope
|
|
|
+ List<String> mutableExcludedScopes = null;
|
|
|
+ if (excludedScopes != null && !excludedScopes.isEmpty()) {
|
|
|
+ mutableExcludedScopes = new ArrayList<>();
|
|
|
+ for (String s: excludedScopes) {
|
|
|
+ // excludeScope covers finalScope
|
|
|
+ if (finalScope.startsWith(s)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // excludeScope and finalScope share nothing case
|
|
|
+ if (s.startsWith(finalScope)) {
|
|
|
+ if (!mutableExcludedScopes.stream().anyMatch(
|
|
|
+ e -> s.startsWith(e))) {
|
|
|
+ mutableExcludedScopes.add(s);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// clone excludedNodes before remove duplicate in it
|
|
|
Collection<Node> mutableExNodes = null;
|
|
|
+
|
|
|
+ // Remove duplicate in excludedNodes
|
|
|
if (excludedNodes != null) {
|
|
|
- // Remove duplicate in excludedNodes
|
|
|
mutableExNodes =
|
|
|
excludedNodes.stream().distinct().collect(Collectors.toList());
|
|
|
}
|
|
|
|
|
|
- // remove duplicate in mutableExNodes and excludedScope, given ancestorGen
|
|
|
- excludedScope = NetUtils.removeDuplicate(this, mutableExNodes,
|
|
|
- excludedScope, ancestorGen);
|
|
|
+ // remove duplicate in mutableExNodes and mutableExcludedScopes
|
|
|
+ NetUtils.removeDuplicate(this, mutableExNodes, mutableExcludedScopes,
|
|
|
+ ancestorGen);
|
|
|
|
|
|
// calculate available node count
|
|
|
Node scopeNode = getNode(finalScope);
|
|
|
int availableNodes = getAvailableNodesCount(
|
|
|
- scopeNode.getNetworkFullPath(), excludedScope, mutableExNodes,
|
|
|
+ scopeNode.getNetworkFullPath(), mutableExcludedScopes, mutableExNodes,
|
|
|
ancestorGen);
|
|
|
|
|
|
if (availableNodes <= 0) {
|
|
|
LOG.warn("No available node in (scope=\"{}\" excludedScope=\"{}\" " +
|
|
|
"excludedNodes=\"{}\" ancestorGen=\"{}\").",
|
|
|
- scopeNode.getNetworkFullPath(), excludedScope, excludedNodes,
|
|
|
+ scopeNode.getNetworkFullPath(), excludedScopes, excludedNodes,
|
|
|
ancestorGen);
|
|
|
return null;
|
|
|
}
|
|
@@ -556,17 +551,17 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
int nodeIndex;
|
|
|
if (leafIndex >= 0) {
|
|
|
nodeIndex = leafIndex % availableNodes;
|
|
|
- ret = ((InnerNode)scopeNode).getLeaf(nodeIndex, excludedScope,
|
|
|
+ ret = ((InnerNode)scopeNode).getLeaf(nodeIndex, mutableExcludedScopes,
|
|
|
mutableExNodes, ancestorGen);
|
|
|
} else {
|
|
|
nodeIndex = ThreadLocalRandom.current().nextInt(availableNodes);
|
|
|
- ret = ((InnerNode)scopeNode).getLeaf(nodeIndex, excludedScope,
|
|
|
+ ret = ((InnerNode)scopeNode).getLeaf(nodeIndex, mutableExcludedScopes,
|
|
|
mutableExNodes, ancestorGen);
|
|
|
}
|
|
|
LOG.debug("Choosing node[index={},random={}] from \"{}\" available nodes" +
|
|
|
" scope=\"{}\", excludedScope=\"{}\", excludeNodes=\"{}\".",
|
|
|
nodeIndex, (leafIndex == -1 ? "true" : "false"), availableNodes,
|
|
|
- scopeNode.getNetworkFullPath(), excludedScope, excludedNodes);
|
|
|
+ scopeNode.getNetworkFullPath(), excludedScopes, excludedNodes);
|
|
|
LOG.debug("Chosen node = {}", (ret == null ? "not found" : ret.toString()));
|
|
|
return ret;
|
|
|
}
|
|
@@ -678,13 +673,13 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
* Return the number of leaves in <i>scope</i> but not in
|
|
|
* <i>excludedNodes</i> and <i>excludeScope</i>.
|
|
|
* @param scope the scope
|
|
|
- * @param excludedScope excluded scope
|
|
|
+ * @param excludedScopes excluded scopes
|
|
|
* @param mutableExcludedNodes a list of excluded nodes, content might be
|
|
|
* changed after the call
|
|
|
* @param ancestorGen same generation ancestor prohibit on excludedNodes
|
|
|
* @return number of available nodes
|
|
|
*/
|
|
|
- private int getAvailableNodesCount(String scope, String excludedScope,
|
|
|
+ private int getAvailableNodesCount(String scope, List<String> excludedScopes,
|
|
|
Collection<Node> mutableExcludedNodes, int ancestorGen) {
|
|
|
Preconditions.checkArgument(scope != null);
|
|
|
|
|
@@ -702,13 +697,15 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
}
|
|
|
// number of nodes to exclude
|
|
|
int excludedCount = 0;
|
|
|
- if (excludedScope != null) {
|
|
|
- Node excludedScopeNode = getNode(excludedScope);
|
|
|
- if (excludedScopeNode != null) {
|
|
|
- if (excludedScope.startsWith(scope)) {
|
|
|
- excludedCount += excludedScopeNode.getNumOfLeaves();
|
|
|
- } else if (scope.startsWith(excludedScope)) {
|
|
|
- return 0;
|
|
|
+ if (excludedScopes != null) {
|
|
|
+ for (String excludedScope: excludedScopes) {
|
|
|
+ Node excludedScopeNode = getNode(excludedScope);
|
|
|
+ if (excludedScopeNode != null) {
|
|
|
+ if (excludedScope.startsWith(scope)) {
|
|
|
+ excludedCount += excludedScopeNode.getNumOfLeaves();
|
|
|
+ } else if (scope.startsWith(excludedScope)) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -766,11 +763,14 @@ public class NetworkTopologyImpl implements NetworkTopology{
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void checkExcludedScope(String excludedScope) {
|
|
|
- if (excludedScope != null &&
|
|
|
- (excludedScope.startsWith(SCOPE_REVERSE_STR))) {
|
|
|
- throw new IllegalArgumentException("excludedScope " + excludedScope +
|
|
|
- " cannot start with " + SCOPE_REVERSE_STR);
|
|
|
+ private void checkExcludedScopes(List<String> excludedScopes) {
|
|
|
+ if (!CollectionUtils.isEmpty(excludedScopes)) {
|
|
|
+ excludedScopes.stream().forEach(scope -> {
|
|
|
+ if (scope.startsWith(SCOPE_REVERSE_STR)) {
|
|
|
+ throw new IllegalArgumentException("excludedScope " + scope +
|
|
|
+ " cannot start with " + SCOPE_REVERSE_STR);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
|