use of com.datatorrent.api.AffinityRulesSet in project apex-core by apache.
the class LogicalPlan method validateAffinityRules.
/**
* validation for affinity rules validates following:
* 1. The operator names specified in affinity rule are part of the dag
* 2. Affinity rules do not conflict with anti-affinity rules directly or indirectly
* 3. Anti-affinity rules do not conflict with Stream Locality
* 4. Anti-affinity rules do not conflict with host-locality attribute
* 5. Affinity rule between non stream operators does not have Thread_Local locality
* 6. Affinity rules do not conflict with host-locality attribute
*/
private void validateAffinityRules() {
AffinityRulesSet affinityRuleSet = getAttributes().get(DAGContext.AFFINITY_RULES_SET);
if (affinityRuleSet == null || affinityRuleSet.getAffinityRules() == null) {
return;
}
Collection<AffinityRule> affinityRules = affinityRuleSet.getAffinityRules();
HashMap<String, Set<String>> containerAffinities = new HashMap<>();
HashMap<String, Set<String>> nodeAffinities = new HashMap<>();
HashMap<String, String> hostNamesMapping = new HashMap<>();
HashMap<OperatorPair, AffinityRule> affinities = new HashMap<>();
HashMap<OperatorPair, AffinityRule> antiAffinities = new HashMap<>();
HashMap<OperatorPair, AffinityRule> threadLocalAffinities = new HashMap<>();
List<String> operatorNames = new ArrayList<>();
for (OperatorMeta operator : getAllOperators()) {
operatorNames.add(operator.getName());
Set<String> containerSet = new HashSet<>();
containerSet.add(operator.getName());
containerAffinities.put(operator.getName(), containerSet);
Set<String> nodeSet = new HashSet<>();
nodeSet.add(operator.getName());
nodeAffinities.put(operator.getName(), nodeSet);
if (operator.getAttributes().get(OperatorContext.LOCALITY_HOST) != null) {
hostNamesMapping.put(operator.getName(), operator.getAttributes().get(OperatorContext.LOCALITY_HOST));
}
}
// Identify operators set as Regex and add to list
for (AffinityRule rule : affinityRules) {
if (rule.getOperatorRegex() != null) {
convertRegexToList(operatorNames, rule);
}
}
// Convert operators with list of operator to rules with operator pairs for validation
for (AffinityRule rule : affinityRules) {
if (rule.getOperatorsList() != null) {
List<String> list = rule.getOperatorsList();
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
OperatorPair pair = new OperatorPair(list.get(i), list.get(j));
if (rule.getType() == com.datatorrent.api.AffinityRule.Type.AFFINITY) {
addToMap(affinities, rule, pair);
} else {
addToMap(antiAffinities, rule, pair);
}
}
}
}
}
for (Entry<OperatorPair, AffinityRule> ruleEntry : affinities.entrySet()) {
OperatorPair pair = ruleEntry.getKey();
AffinityRule rule = ruleEntry.getValue();
if (hostNamesMapping.containsKey(pair.first) && hostNamesMapping.containsKey(pair.second) && !hostNamesMapping.get(pair.first).equals(hostNamesMapping.get(pair.second))) {
throw new ValidationException(String.format("Host Locality for operators: %s(host: %s) & %s(host: %s) conflicts with affinity rules", pair.first, hostNamesMapping.get(pair.first), pair.second, hostNamesMapping.get(pair.second)));
}
if (rule.getLocality() == Locality.THREAD_LOCAL) {
addToMap(threadLocalAffinities, rule, pair);
} else if (rule.getLocality() == Locality.CONTAINER_LOCAL) {
// Combine the sets
combineSets(containerAffinities, pair);
// Also update node list
combineSets(nodeAffinities, pair);
} else if (rule.getLocality() == Locality.NODE_LOCAL) {
combineSets(nodeAffinities, pair);
}
}
for (StreamMeta stream : getAllStreams()) {
String source = stream.source.getOperatorMeta().getName();
for (InputPortMeta sink : stream.sinks) {
String sinkOperator = sink.getOperatorMeta().getName();
OperatorPair pair = new OperatorPair(source, sinkOperator);
if (stream.getLocality() != null && stream.getLocality().ordinal() <= Locality.NODE_LOCAL.ordinal() && hostNamesMapping.containsKey(pair.first) && hostNamesMapping.containsKey(pair.second) && !hostNamesMapping.get(pair.first).equals(hostNamesMapping.get(pair.second))) {
throw new ValidationException(String.format("Host Locality for operators: %s(host: %s) & %s(host: %s) conflicts with stream locality", pair.first, hostNamesMapping.get(pair.first), pair.second, hostNamesMapping.get(pair.second)));
}
if (stream.locality == Locality.CONTAINER_LOCAL) {
combineSets(containerAffinities, pair);
combineSets(nodeAffinities, pair);
} else if (stream.locality == Locality.NODE_LOCAL) {
combineSets(nodeAffinities, pair);
}
if (affinities.containsKey(pair)) {
// Choose the lower bound on locality
AffinityRule rule = affinities.get(pair);
if (rule.getLocality() == Locality.THREAD_LOCAL) {
stream.setLocality(rule.getLocality());
threadLocalAffinities.remove(rule);
affinityRules.remove(rule);
}
if (stream.locality != null && rule.getLocality().ordinal() > stream.getLocality().ordinal()) {
// Remove the affinity rule from attributes, as it is redundant
affinityRules.remove(rule);
}
}
}
}
// Validate that all Thread local affinities were for stream connected operators
if (!threadLocalAffinities.isEmpty()) {
OperatorPair pair = threadLocalAffinities.keySet().iterator().next();
throw new ValidationException(String.format("Affinity rule specified THREAD_LOCAL affinity for operators %s & %s which are not connected by stream", pair.first, pair.second));
}
for (Entry<OperatorPair, AffinityRule> ruleEntry : antiAffinities.entrySet()) {
OperatorPair pair = ruleEntry.getKey();
AffinityRule rule = ruleEntry.getValue();
if (pair.first.equals(pair.second)) {
continue;
}
if (rule.getLocality() == Locality.CONTAINER_LOCAL) {
if (containerAffinities.get(pair.first).contains(pair.second)) {
throw new ValidationException(String.format("Anti Affinity rule for operators %s & %s conflicts with affinity rules or Stream locality", pair.first, pair.second));
}
} else if (rule.getLocality() == Locality.NODE_LOCAL) {
if (nodeAffinities.get(pair.first).contains(pair.second)) {
throw new ValidationException(String.format("Anti Affinity rule for operators %s & %s conflicts with affinity rules or Stream locality", pair.first, pair.second));
}
// Check host locality for both operators
// Check host attribute for all operators in node local set for both
// anti-affinity operators
String firstOperatorLocality = getHostLocality(nodeAffinities, pair.first, hostNamesMapping);
String secondOperatorLocality = getHostLocality(nodeAffinities, pair.second, hostNamesMapping);
if (firstOperatorLocality != null && secondOperatorLocality != null && firstOperatorLocality == secondOperatorLocality) {
throw new ValidationException(String.format("Host Locality for operators: %s(host: %s) & %s(host: %s) conflict with anti-affinity rules", pair.first, firstOperatorLocality, pair.second, secondOperatorLocality));
}
}
}
}
Aggregations