YARN-5325. Stateless ARMRMProxy policies implementation. (Carlo Curino via Subru).
(cherry picked from commit 11c5336522
)
This commit is contained in:
parent
0662996b6a
commit
1dadd0b45a
|
@ -1,4 +1,4 @@
|
||||||
/**
|
/*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
|
@ -16,82 +16,87 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.router;
|
package org.apache.hadoop.yarn.server.federation.policies;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.FederationPolicyInitializationContext;
|
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.FederationPolicyInitializationContextValidator;
|
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyInitializationException;
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyInitializationException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.exceptions.NoActiveSubclustersException;
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.NoActiveSubclustersException;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterIdInfo;
|
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class provides common validation of reinitialize(), for all
|
* Base abstract class for a weighted {@link ConfigurableFederationPolicy}.
|
||||||
* policies that are "weight-based".
|
|
||||||
*/
|
*/
|
||||||
public abstract class BaseWeightedRouterPolicy
|
public abstract class AbstractConfigurableFederationPolicy
|
||||||
implements FederationRouterPolicy {
|
implements ConfigurableFederationPolicy {
|
||||||
|
|
||||||
private WeightedPolicyInfo policyInfo = null;
|
private WeightedPolicyInfo policyInfo = null;
|
||||||
private FederationPolicyInitializationContext policyContext;
|
private FederationPolicyInitializationContext policyContext;
|
||||||
|
private boolean isDirty;
|
||||||
|
|
||||||
public BaseWeightedRouterPolicy() {
|
public AbstractConfigurableFederationPolicy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reinitialize(FederationPolicyInitializationContext
|
public void reinitialize(
|
||||||
federationPolicyContext)
|
FederationPolicyInitializationContext initializationContext)
|
||||||
throws FederationPolicyInitializationException {
|
throws FederationPolicyInitializationException {
|
||||||
|
isDirty = true;
|
||||||
FederationPolicyInitializationContextValidator
|
FederationPolicyInitializationContextValidator
|
||||||
.validate(federationPolicyContext, this.getClass().getCanonicalName());
|
.validate(initializationContext, this.getClass().getCanonicalName());
|
||||||
|
|
||||||
// perform consistency checks
|
// perform consistency checks
|
||||||
WeightedPolicyInfo newPolicyInfo = WeightedPolicyInfo
|
WeightedPolicyInfo newPolicyInfo = WeightedPolicyInfo.fromByteBuffer(
|
||||||
.fromByteBuffer(
|
initializationContext.getSubClusterPolicyConfiguration().getParams());
|
||||||
federationPolicyContext.getSubClusterPolicyConfiguration()
|
|
||||||
.getParams());
|
|
||||||
|
|
||||||
// if nothing has changed skip the rest of initialization
|
// if nothing has changed skip the rest of initialization
|
||||||
|
// and signal to childs that the reinit is free via isDirty var.
|
||||||
if (policyInfo != null && policyInfo.equals(newPolicyInfo)) {
|
if (policyInfo != null && policyInfo.equals(newPolicyInfo)) {
|
||||||
|
isDirty = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(newPolicyInfo);
|
validate(newPolicyInfo);
|
||||||
setPolicyInfo(newPolicyInfo);
|
setPolicyInfo(newPolicyInfo);
|
||||||
this.policyContext = federationPolicyContext;
|
this.policyContext = initializationContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridable validation step for the policy configuration.
|
* Overridable validation step for the policy configuration.
|
||||||
|
*
|
||||||
* @param newPolicyInfo the configuration to test.
|
* @param newPolicyInfo the configuration to test.
|
||||||
* @throws FederationPolicyInitializationException if the configuration is
|
*
|
||||||
* not valid.
|
* @throws FederationPolicyInitializationException if the configuration is not
|
||||||
|
* valid.
|
||||||
*/
|
*/
|
||||||
public void validate(WeightedPolicyInfo newPolicyInfo) throws
|
public void validate(WeightedPolicyInfo newPolicyInfo)
|
||||||
FederationPolicyInitializationException {
|
throws FederationPolicyInitializationException {
|
||||||
if (newPolicyInfo == null) {
|
if (newPolicyInfo == null) {
|
||||||
throw new FederationPolicyInitializationException("The policy to "
|
|
||||||
+ "validate should not be null.");
|
|
||||||
}
|
|
||||||
Map<SubClusterIdInfo, Float> newWeights =
|
|
||||||
newPolicyInfo.getRouterPolicyWeights();
|
|
||||||
if (newWeights == null || newWeights.size() < 1) {
|
|
||||||
throw new FederationPolicyInitializationException(
|
throw new FederationPolicyInitializationException(
|
||||||
"Weight vector cannot be null/empty.");
|
"The policy to " + "validate should not be null.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true whether the last reinitialization requires actual changes, or
|
||||||
|
* was "free" as the weights have not changed. This is used by subclasses
|
||||||
|
* overriding reinitialize and calling super.reinitialize() to know wheter to
|
||||||
|
* quit early.
|
||||||
|
*
|
||||||
|
* @return whether more work is needed to initialize.
|
||||||
|
*/
|
||||||
|
public boolean getIsDirty() {
|
||||||
|
return isDirty;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter method for the configuration weights.
|
* Getter method for the configuration weights.
|
||||||
*
|
*
|
||||||
* @return the {@link WeightedPolicyInfo} representing the policy
|
* @return the {@link WeightedPolicyInfo} representing the policy
|
||||||
* configuration.
|
* configuration.
|
||||||
*/
|
*/
|
||||||
public WeightedPolicyInfo getPolicyInfo() {
|
public WeightedPolicyInfo getPolicyInfo() {
|
||||||
return policyInfo;
|
return policyInfo;
|
||||||
|
@ -101,15 +106,15 @@ public WeightedPolicyInfo getPolicyInfo() {
|
||||||
* Setter method for the configuration weights.
|
* Setter method for the configuration weights.
|
||||||
*
|
*
|
||||||
* @param policyInfo the {@link WeightedPolicyInfo} representing the policy
|
* @param policyInfo the {@link WeightedPolicyInfo} representing the policy
|
||||||
* configuration.
|
* configuration.
|
||||||
*/
|
*/
|
||||||
public void setPolicyInfo(
|
public void setPolicyInfo(WeightedPolicyInfo policyInfo) {
|
||||||
WeightedPolicyInfo policyInfo) {
|
|
||||||
this.policyInfo = policyInfo;
|
this.policyInfo = policyInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter method for the {@link FederationPolicyInitializationContext}.
|
* Getter method for the {@link FederationPolicyInitializationContext}.
|
||||||
|
*
|
||||||
* @return the context for this policy.
|
* @return the context for this policy.
|
||||||
*/
|
*/
|
||||||
public FederationPolicyInitializationContext getPolicyContext() {
|
public FederationPolicyInitializationContext getPolicyContext() {
|
||||||
|
@ -118,6 +123,7 @@ public FederationPolicyInitializationContext getPolicyContext() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setter method for the {@link FederationPolicyInitializationContext}.
|
* Setter method for the {@link FederationPolicyInitializationContext}.
|
||||||
|
*
|
||||||
* @param policyContext the context to assign to this policy.
|
* @param policyContext the context to assign to this policy.
|
||||||
*/
|
*/
|
||||||
public void setPolicyContext(
|
public void setPolicyContext(
|
||||||
|
@ -130,13 +136,14 @@ public void setPolicyContext(
|
||||||
* FederationStateStoreFacade} and validate it not being null/empty.
|
* FederationStateStoreFacade} and validate it not being null/empty.
|
||||||
*
|
*
|
||||||
* @return the map of ids to info for all active subclusters.
|
* @return the map of ids to info for all active subclusters.
|
||||||
|
*
|
||||||
* @throws YarnException if we can't get the list.
|
* @throws YarnException if we can't get the list.
|
||||||
*/
|
*/
|
||||||
protected Map<SubClusterId, SubClusterInfo> getActiveSubclusters()
|
protected Map<SubClusterId, SubClusterInfo> getActiveSubclusters()
|
||||||
throws YarnException {
|
throws YarnException {
|
||||||
|
|
||||||
Map<SubClusterId, SubClusterInfo> activeSubclusters = getPolicyContext()
|
Map<SubClusterId, SubClusterInfo> activeSubclusters =
|
||||||
.getFederationStateStoreFacade().getSubClusters(true);
|
getPolicyContext().getFederationStateStoreFacade().getSubClusters(true);
|
||||||
|
|
||||||
if (activeSubclusters == null || activeSubclusters.size() < 1) {
|
if (activeSubclusters == null || activeSubclusters.size() < 1) {
|
||||||
throw new NoActiveSubclustersException(
|
throw new NoActiveSubclustersException(
|
||||||
|
@ -145,6 +152,4 @@ protected Map<SubClusterId, SubClusterInfo> getActiveSubclusters()
|
||||||
return activeSubclusters;
|
return activeSubclusters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -31,14 +31,11 @@ public interface ConfigurableFederationPolicy {
|
||||||
* policies. The implementor should provide try-n-swap semantics, and retain
|
* policies. The implementor should provide try-n-swap semantics, and retain
|
||||||
* state if possible.
|
* state if possible.
|
||||||
*
|
*
|
||||||
* @param federationPolicyInitializationContext the new context to provide to
|
* @param policyContext the new context to provide to implementor.
|
||||||
* implementor.
|
|
||||||
*
|
*
|
||||||
* @throws FederationPolicyInitializationException in case the initialization
|
* @throws FederationPolicyInitializationException in case the initialization
|
||||||
* fails.
|
* fails.
|
||||||
*/
|
*/
|
||||||
void reinitialize(
|
void reinitialize(FederationPolicyInitializationContext policyContext)
|
||||||
FederationPolicyInitializationContext
|
|
||||||
federationPolicyInitializationContext)
|
|
||||||
throws FederationPolicyInitializationException;
|
throws FederationPolicyInitializationException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.apache.hadoop.yarn.server.federation.policies;
|
package org.apache.hadoop.yarn.server.federation.policies;
|
||||||
|
|
||||||
import org.apache.hadoop.yarn.server.federation.resolver.SubClusterResolver;
|
import org.apache.hadoop.yarn.server.federation.resolver.SubClusterResolver;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterPolicyConfiguration;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterPolicyConfiguration;
|
||||||
import org.apache.hadoop.yarn.server.federation.utils.FederationStateStoreFacade;
|
import org.apache.hadoop.yarn.server.federation.utils.FederationStateStoreFacade;
|
||||||
|
|
||||||
|
@ -30,6 +31,7 @@ public class FederationPolicyInitializationContext {
|
||||||
private SubClusterPolicyConfiguration federationPolicyConfiguration;
|
private SubClusterPolicyConfiguration federationPolicyConfiguration;
|
||||||
private SubClusterResolver federationSubclusterResolver;
|
private SubClusterResolver federationSubclusterResolver;
|
||||||
private FederationStateStoreFacade federationStateStoreFacade;
|
private FederationStateStoreFacade federationStateStoreFacade;
|
||||||
|
private SubClusterId homeSubcluster;
|
||||||
|
|
||||||
public FederationPolicyInitializationContext() {
|
public FederationPolicyInitializationContext() {
|
||||||
federationPolicyConfiguration = null;
|
federationPolicyConfiguration = null;
|
||||||
|
@ -37,20 +39,19 @@ public FederationPolicyInitializationContext() {
|
||||||
federationStateStoreFacade = null;
|
federationStateStoreFacade = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FederationPolicyInitializationContext(SubClusterPolicyConfiguration
|
public FederationPolicyInitializationContext(
|
||||||
policy, SubClusterResolver resolver, FederationStateStoreFacade
|
SubClusterPolicyConfiguration policy, SubClusterResolver resolver,
|
||||||
storeFacade) {
|
FederationStateStoreFacade storeFacade) {
|
||||||
this.federationPolicyConfiguration = policy;
|
this.federationPolicyConfiguration = policy;
|
||||||
this.federationSubclusterResolver = resolver;
|
this.federationSubclusterResolver = resolver;
|
||||||
this.federationStateStoreFacade = storeFacade;
|
this.federationStateStoreFacade = storeFacade;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for the {@link SubClusterPolicyConfiguration}.
|
* Getter for the {@link SubClusterPolicyConfiguration}.
|
||||||
*
|
*
|
||||||
* @return the {@link SubClusterPolicyConfiguration} to be used for
|
* @return the {@link SubClusterPolicyConfiguration} to be used for
|
||||||
* initialization.
|
* initialization.
|
||||||
*/
|
*/
|
||||||
public SubClusterPolicyConfiguration getSubClusterPolicyConfiguration() {
|
public SubClusterPolicyConfiguration getSubClusterPolicyConfiguration() {
|
||||||
return federationPolicyConfiguration;
|
return federationPolicyConfiguration;
|
||||||
|
@ -59,8 +60,8 @@ public SubClusterPolicyConfiguration getSubClusterPolicyConfiguration() {
|
||||||
/**
|
/**
|
||||||
* Setter for the {@link SubClusterPolicyConfiguration}.
|
* Setter for the {@link SubClusterPolicyConfiguration}.
|
||||||
*
|
*
|
||||||
* @param fedPolicyConfiguration the {@link SubClusterPolicyConfiguration}
|
* @param fedPolicyConfiguration the {@link SubClusterPolicyConfiguration} to
|
||||||
* to be used for initialization.
|
* be used for initialization.
|
||||||
*/
|
*/
|
||||||
public void setSubClusterPolicyConfiguration(
|
public void setSubClusterPolicyConfiguration(
|
||||||
SubClusterPolicyConfiguration fedPolicyConfiguration) {
|
SubClusterPolicyConfiguration fedPolicyConfiguration) {
|
||||||
|
@ -80,7 +81,7 @@ public SubClusterResolver getFederationSubclusterResolver() {
|
||||||
* Setter for the {@link SubClusterResolver}.
|
* Setter for the {@link SubClusterResolver}.
|
||||||
*
|
*
|
||||||
* @param federationSubclusterResolver the {@link SubClusterResolver} to be
|
* @param federationSubclusterResolver the {@link SubClusterResolver} to be
|
||||||
* used for initialization.
|
* used for initialization.
|
||||||
*/
|
*/
|
||||||
public void setFederationSubclusterResolver(
|
public void setFederationSubclusterResolver(
|
||||||
SubClusterResolver federationSubclusterResolver) {
|
SubClusterResolver federationSubclusterResolver) {
|
||||||
|
@ -105,4 +106,24 @@ public void setFederationStateStoreFacade(
|
||||||
FederationStateStoreFacade federationStateStoreFacade) {
|
FederationStateStoreFacade federationStateStoreFacade) {
|
||||||
this.federationStateStoreFacade = federationStateStoreFacade;
|
this.federationStateStoreFacade = federationStateStoreFacade;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current home sub-cluster. Useful for default policy behaviors.
|
||||||
|
*
|
||||||
|
* @return the home sub-cluster.
|
||||||
|
*/
|
||||||
|
public SubClusterId getHomeSubcluster() {
|
||||||
|
return homeSubcluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets in the context the home sub-cluster. Useful for default policy
|
||||||
|
* behaviors.
|
||||||
|
*
|
||||||
|
* @param homeSubcluster value to set.
|
||||||
|
*/
|
||||||
|
public void setHomeSubcluster(SubClusterId homeSubcluster) {
|
||||||
|
this.homeSubcluster = homeSubcluster;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,50 +25,44 @@
|
||||||
public final class FederationPolicyInitializationContextValidator {
|
public final class FederationPolicyInitializationContextValidator {
|
||||||
|
|
||||||
private FederationPolicyInitializationContextValidator() {
|
private FederationPolicyInitializationContextValidator() {
|
||||||
//disable constructor per checkstyle
|
// disable constructor per checkstyle
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void validate(
|
public static void validate(
|
||||||
FederationPolicyInitializationContext
|
FederationPolicyInitializationContext policyContext, String myType)
|
||||||
federationPolicyInitializationContext,
|
throws FederationPolicyInitializationException {
|
||||||
String myType) throws FederationPolicyInitializationException {
|
|
||||||
|
|
||||||
if (myType == null) {
|
if (myType == null) {
|
||||||
throw new FederationPolicyInitializationException("The myType parameter"
|
throw new FederationPolicyInitializationException(
|
||||||
+ " should not be null.");
|
"The myType parameter" + " should not be null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (federationPolicyInitializationContext == null) {
|
if (policyContext == null) {
|
||||||
throw new FederationPolicyInitializationException(
|
throw new FederationPolicyInitializationException(
|
||||||
"The FederationPolicyInitializationContext provided is null. Cannot"
|
"The FederationPolicyInitializationContext provided is null. Cannot"
|
||||||
+ " reinitalize "
|
+ " reinitalize " + "successfully.");
|
||||||
+ "successfully.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (federationPolicyInitializationContext.getFederationStateStoreFacade()
|
if (policyContext.getFederationStateStoreFacade() == null) {
|
||||||
== null) {
|
|
||||||
throw new FederationPolicyInitializationException(
|
throw new FederationPolicyInitializationException(
|
||||||
"The FederationStateStoreFacade provided is null. Cannot"
|
"The FederationStateStoreFacade provided is null. Cannot"
|
||||||
+ " reinitalize successfully.");
|
+ " reinitalize successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (federationPolicyInitializationContext.getFederationSubclusterResolver()
|
if (policyContext.getFederationSubclusterResolver() == null) {
|
||||||
== null) {
|
|
||||||
throw new FederationPolicyInitializationException(
|
throw new FederationPolicyInitializationException(
|
||||||
"The FederationStateStoreFacase provided is null. Cannot"
|
"The FederationStateStoreFacase provided is null. Cannot"
|
||||||
+ " reinitalize successfully.");
|
+ " reinitalize successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (federationPolicyInitializationContext.getSubClusterPolicyConfiguration()
|
if (policyContext.getSubClusterPolicyConfiguration() == null) {
|
||||||
== null) {
|
|
||||||
throw new FederationPolicyInitializationException(
|
throw new FederationPolicyInitializationException(
|
||||||
"The FederationSubclusterResolver provided is null. Cannot "
|
"The FederationSubclusterResolver provided is null. Cannot "
|
||||||
+ "reinitalize successfully.");
|
+ "reinitalize successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
String intendedType =
|
String intendedType =
|
||||||
federationPolicyInitializationContext.getSubClusterPolicyConfiguration()
|
policyContext.getSubClusterPolicyConfiguration().getType();
|
||||||
.getType();
|
|
||||||
|
|
||||||
if (!myType.equals(intendedType)) {
|
if (!myType.equals(intendedType)) {
|
||||||
throw new FederationPolicyInitializationException(
|
throw new FederationPolicyInitializationException(
|
||||||
|
|
|
@ -25,19 +25,19 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Implementors need to provide the ability to serliaze a policy and its
|
* Implementors need to provide the ability to serliaze a policy and its
|
||||||
* configuration as a {@link SubClusterPolicyConfiguration}, as well as
|
* configuration as a {@link SubClusterPolicyConfiguration}, as well as provide
|
||||||
* provide (re)initialization mechanics for the underlying
|
* (re)initialization mechanics for the underlying
|
||||||
* {@link FederationAMRMProxyPolicy} and {@link FederationRouterPolicy}.
|
* {@link FederationAMRMProxyPolicy} and {@link FederationRouterPolicy}.
|
||||||
*
|
*
|
||||||
* The serialization aspects are used by admin APIs or a policy engine to
|
* The serialization aspects are used by admin APIs or a policy engine to store
|
||||||
* store a serialized configuration in the {@code FederationStateStore},
|
* a serialized configuration in the {@code FederationStateStore}, while the
|
||||||
* while the getters methods are used to obtain a propertly inizialized
|
* getters methods are used to obtain a propertly inizialized policy in the
|
||||||
* policy in the {@code Router} and {@code AMRMProxy} respectively.
|
* {@code Router} and {@code AMRMProxy} respectively.
|
||||||
*
|
*
|
||||||
* This interface by design binds together
|
* This interface by design binds together {@link FederationAMRMProxyPolicy} and
|
||||||
* {@link FederationAMRMProxyPolicy} and {@link FederationRouterPolicy} and
|
* {@link FederationRouterPolicy} and provide lifecycle support for
|
||||||
* provide lifecycle support for serialization and deserialization, to reduce
|
* serialization and deserialization, to reduce configuration mistakes
|
||||||
* configuration mistakes (combining incompatible policies).
|
* (combining incompatible policies).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public interface FederationPolicyManager {
|
public interface FederationPolicyManager {
|
||||||
|
@ -50,23 +50,17 @@ public interface FederationPolicyManager {
|
||||||
* the implementors should attempt to reinitalize (retaining state). To affect
|
* the implementors should attempt to reinitalize (retaining state). To affect
|
||||||
* a complete policy reset oldInstance should be null.
|
* a complete policy reset oldInstance should be null.
|
||||||
*
|
*
|
||||||
* @param federationPolicyInitializationContext the current context
|
* @param policyContext the current context
|
||||||
* @param oldInstance the existing (possibly null)
|
* @param oldInstance the existing (possibly null) instance.
|
||||||
* instance.
|
|
||||||
*
|
*
|
||||||
* @return an updated {@link FederationAMRMProxyPolicy
|
* @return an updated {@link FederationAMRMProxyPolicy }.
|
||||||
}.
|
|
||||||
*
|
*
|
||||||
* @throws FederationPolicyInitializationException if the initialization
|
* @throws FederationPolicyInitializationException if the initialization
|
||||||
* cannot be completed
|
* cannot be completed properly. The oldInstance should be still
|
||||||
* properly. The oldInstance
|
* valid in case of failed initialization.
|
||||||
* should be still valid in
|
|
||||||
* case of failed
|
|
||||||
* initialization.
|
|
||||||
*/
|
*/
|
||||||
FederationAMRMProxyPolicy getAMRMPolicy(
|
FederationAMRMProxyPolicy getAMRMPolicy(
|
||||||
FederationPolicyInitializationContext
|
FederationPolicyInitializationContext policyContext,
|
||||||
federationPolicyInitializationContext,
|
|
||||||
FederationAMRMProxyPolicy oldInstance)
|
FederationAMRMProxyPolicy oldInstance)
|
||||||
throws FederationPolicyInitializationException;
|
throws FederationPolicyInitializationException;
|
||||||
|
|
||||||
|
@ -78,21 +72,17 @@ FederationAMRMProxyPolicy getAMRMPolicy(
|
||||||
* implementors should attempt to reinitalize (retaining state). To affect a
|
* implementors should attempt to reinitalize (retaining state). To affect a
|
||||||
* complete policy reset oldInstance shoulb be set to null.
|
* complete policy reset oldInstance shoulb be set to null.
|
||||||
*
|
*
|
||||||
* @param federationPolicyInitializationContext the current context
|
* @param policyContext the current context
|
||||||
* @param oldInstance the existing (possibly null)
|
* @param oldInstance the existing (possibly null) instance.
|
||||||
* instance.
|
|
||||||
*
|
*
|
||||||
* @return an updated {@link FederationRouterPolicy}.
|
* @return an updated {@link FederationRouterPolicy}.
|
||||||
*
|
*
|
||||||
* @throws FederationPolicyInitializationException if the initalization cannot
|
* @throws FederationPolicyInitializationException if the initalization cannot
|
||||||
* be completed properly. The
|
* be completed properly. The oldInstance should be still valid in
|
||||||
* oldInstance should be still
|
* case of failed initialization.
|
||||||
* valid in case of failed
|
|
||||||
* initialization.
|
|
||||||
*/
|
*/
|
||||||
FederationRouterPolicy getRouterPolicy(
|
FederationRouterPolicy getRouterPolicy(
|
||||||
FederationPolicyInitializationContext
|
FederationPolicyInitializationContext policyContext,
|
||||||
federationPolicyInitializationContext,
|
|
||||||
FederationRouterPolicy oldInstance)
|
FederationRouterPolicy oldInstance)
|
||||||
throws FederationPolicyInitializationException;
|
throws FederationPolicyInitializationException;
|
||||||
|
|
||||||
|
@ -102,23 +92,24 @@ FederationRouterPolicy getRouterPolicy(
|
||||||
* store.
|
* store.
|
||||||
*
|
*
|
||||||
* @return a valid policy configuration representing this object
|
* @return a valid policy configuration representing this object
|
||||||
* parametrization.
|
* parametrization.
|
||||||
*
|
*
|
||||||
* @throws FederationPolicyInitializationException if the current state cannot
|
* @throws FederationPolicyInitializationException if the current state cannot
|
||||||
* be serialized properly
|
* be serialized properly
|
||||||
*/
|
*/
|
||||||
SubClusterPolicyConfiguration serializeConf()
|
SubClusterPolicyConfiguration serializeConf()
|
||||||
throws FederationPolicyInitializationException;
|
throws FederationPolicyInitializationException;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns the queue this policy is configured for.
|
* This method returns the queue this policy is configured for.
|
||||||
|
*
|
||||||
* @return the name of the queue.
|
* @return the name of the queue.
|
||||||
*/
|
*/
|
||||||
String getQueue();
|
String getQueue();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This methods provides a setter for the queue this policy is specified for.
|
* This methods provides a setter for the queue this policy is specified for.
|
||||||
|
*
|
||||||
* @param queue the name of the queue.
|
* @param queue the name of the queue.
|
||||||
*/
|
*/
|
||||||
void setQueue(String queue);
|
void setQueue(String queue);
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.yarn.server.federation.policies.amrmproxy;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.AbstractConfigurableFederationPolicy;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyInitializationException;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterIdInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base abstract class for {@link FederationAMRMProxyPolicy} implementations,
|
||||||
|
* that provides common validation for reinitialization.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractAMRMProxyPolicy extends
|
||||||
|
AbstractConfigurableFederationPolicy implements FederationAMRMProxyPolicy {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(WeightedPolicyInfo newPolicyInfo)
|
||||||
|
throws FederationPolicyInitializationException {
|
||||||
|
super.validate(newPolicyInfo);
|
||||||
|
Map<SubClusterIdInfo, Float> newWeights =
|
||||||
|
newPolicyInfo.getAMRMPolicyWeights();
|
||||||
|
if (newWeights == null || newWeights.size() < 1) {
|
||||||
|
throw new FederationPolicyInitializationException(
|
||||||
|
"Weight vector cannot be null/empty.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.yarn.server.federation.policies.amrmproxy;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
|
||||||
|
import org.apache.hadoop.yarn.api.records.ResourceRequest;
|
||||||
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.FederationPolicyInitializationContext;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.FederationPolicyInitializationContextValidator;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyInitializationException;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.UnknownSubclusterException;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of the {@link FederationAMRMProxyPolicy} that simply
|
||||||
|
* broadcasts each {@link ResourceRequest} to all the available sub-clusters.
|
||||||
|
*/
|
||||||
|
public class BroadcastAMRMProxyPolicy extends AbstractAMRMProxyPolicy {
|
||||||
|
|
||||||
|
private Set<SubClusterId> knownClusterIds = new HashSet<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reinitialize(
|
||||||
|
FederationPolicyInitializationContext policyContext)
|
||||||
|
throws FederationPolicyInitializationException {
|
||||||
|
// overrides initialize to avoid weight checks that do no apply for
|
||||||
|
// this policy.
|
||||||
|
FederationPolicyInitializationContextValidator
|
||||||
|
.validate(policyContext, this.getClass().getCanonicalName());
|
||||||
|
setPolicyContext(policyContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<SubClusterId, List<ResourceRequest>> splitResourceRequests(
|
||||||
|
List<ResourceRequest> resourceRequests) throws YarnException {
|
||||||
|
|
||||||
|
Map<SubClusterId, SubClusterInfo> activeSubclusters =
|
||||||
|
getActiveSubclusters();
|
||||||
|
|
||||||
|
Map<SubClusterId, List<ResourceRequest>> answer = new HashMap<>();
|
||||||
|
|
||||||
|
// simply broadcast the resource request to all sub-clusters
|
||||||
|
for (SubClusterId subClusterId : activeSubclusters.keySet()) {
|
||||||
|
answer.put(subClusterId, resourceRequests);
|
||||||
|
knownClusterIds.add(subClusterId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notifyOfResponse(SubClusterId subClusterId,
|
||||||
|
AllocateResponse response) throws YarnException {
|
||||||
|
if (!knownClusterIds.contains(subClusterId)) {
|
||||||
|
throw new UnknownSubclusterException(
|
||||||
|
"The response is received from a subcluster that is unknown to this "
|
||||||
|
+ "policy.");
|
||||||
|
}
|
||||||
|
// stateless policy does not care about responses
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,18 +17,18 @@
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.amrmproxy;
|
package org.apache.hadoop.yarn.server.federation.policies.amrmproxy;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
|
import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
|
||||||
import org.apache.hadoop.yarn.api.records.ResourceRequest;
|
import org.apache.hadoop.yarn.api.records.ResourceRequest;
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.ConfigurableFederationPolicy;
|
import org.apache.hadoop.yarn.server.federation.policies.ConfigurableFederationPolicy;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementors of this interface provide logic to split the list of {@link
|
* Implementors of this interface provide logic to split the list of
|
||||||
* ResourceRequest}s received by the AM among various RMs.
|
* {@link ResourceRequest}s received by the AM among various RMs.
|
||||||
*/
|
*/
|
||||||
public interface FederationAMRMProxyPolicy
|
public interface FederationAMRMProxyPolicy
|
||||||
extends ConfigurableFederationPolicy {
|
extends ConfigurableFederationPolicy {
|
||||||
|
@ -37,18 +37,17 @@ public interface FederationAMRMProxyPolicy
|
||||||
* Splits the {@link ResourceRequest}s from the client across one or more
|
* Splits the {@link ResourceRequest}s from the client across one or more
|
||||||
* sub-clusters based on the policy semantics (e.g., broadcast, load-based).
|
* sub-clusters based on the policy semantics (e.g., broadcast, load-based).
|
||||||
*
|
*
|
||||||
* @param resourceRequests the list of {@link ResourceRequest}s from the
|
* @param resourceRequests the list of {@link ResourceRequest}s from the AM to
|
||||||
* AM to be split
|
* be split
|
||||||
*
|
*
|
||||||
* @return map of sub-cluster as identified by {@link SubClusterId} to the
|
* @return map of sub-cluster as identified by {@link SubClusterId} to the
|
||||||
* list of {@link ResourceRequest}s that should be forwarded to it
|
* list of {@link ResourceRequest}s that should be forwarded to it
|
||||||
*
|
*
|
||||||
* @throws YarnException in case the request is malformed or no viable
|
* @throws YarnException in case the request is malformed or no viable
|
||||||
* sub-clusters can be found.
|
* sub-clusters can be found.
|
||||||
*/
|
*/
|
||||||
Map<SubClusterId, List<ResourceRequest>> splitResourceRequests(
|
Map<SubClusterId, List<ResourceRequest>> splitResourceRequests(
|
||||||
List<ResourceRequest> resourceRequests)
|
List<ResourceRequest> resourceRequests) throws YarnException;
|
||||||
throws YarnException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method should be invoked to notify the policy about responses being
|
* This method should be invoked to notify the policy about responses being
|
||||||
|
@ -60,7 +59,7 @@ Map<SubClusterId, List<ResourceRequest>> splitResourceRequests(
|
||||||
*
|
*
|
||||||
* @throws YarnException in case the response is not valid
|
* @throws YarnException in case the response is not valid
|
||||||
*/
|
*/
|
||||||
void notifyOfResponse(SubClusterId subClusterId,
|
void notifyOfResponse(SubClusterId subClusterId, AllocateResponse response)
|
||||||
AllocateResponse response) throws YarnException;
|
throws YarnException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,583 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.yarn.server.federation.policies.amrmproxy;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
|
||||||
|
import org.apache.hadoop.yarn.api.records.Resource;
|
||||||
|
import org.apache.hadoop.yarn.api.records.ResourceRequest;
|
||||||
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.FederationPolicyInitializationContext;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyInitializationException;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.NoActiveSubclustersException;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.resolver.SubClusterResolver;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterIdInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.utils.FederationStateStoreFacade;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of the {@link FederationAMRMProxyPolicy} interface that
|
||||||
|
* carefully multicasts the requests with the following behavior:
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Host localized {@link ResourceRequest}s are always forwarded to the RM that
|
||||||
|
* owns the corresponding node, based on the feedback of a
|
||||||
|
* {@link SubClusterResolver}. If the {@link SubClusterResolver} cannot resolve
|
||||||
|
* this node we default to forwarding the {@link ResourceRequest} to the home
|
||||||
|
* sub-cluster.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Rack localized {@link ResourceRequest}s are forwarded to the RMs that owns
|
||||||
|
* the corresponding rack. Note that in some deployments each rack could be
|
||||||
|
* striped across multiple RMs. Thsi policy respects that. If the
|
||||||
|
* {@link SubClusterResolver} cannot resolve this rack we default to forwarding
|
||||||
|
* the {@link ResourceRequest} to the home sub-cluster.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* ANY requests corresponding to node/rack local requests are forwarded only to
|
||||||
|
* the set of RMs that owns the corresponding localized requests. The number of
|
||||||
|
* containers listed in each ANY is proportional to the number of localized
|
||||||
|
* container requests (associated to this ANY via the same allocateRequestId).
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* ANY that are not associated to node/rack local requests are split among RMs
|
||||||
|
* based on the "weights" in the {@link WeightedPolicyInfo} configuration *and*
|
||||||
|
* headroom information. The {@code headroomAlpha} parameter of the policy
|
||||||
|
* configuration indicates how much headroom contributes to the splitting
|
||||||
|
* choice. Value of 1.0f indicates the weights are interpreted only as 0/1
|
||||||
|
* boolean but all splitting is based on the advertised headroom (fallback to
|
||||||
|
* 1/N for RMs that we don't have headroom info from). An {@code headroomAlpha}
|
||||||
|
* value of 0.0f means headroom is ignored and all splitting decisions are
|
||||||
|
* proportional to the "weights" in the configuration of the policy.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* ANY of zero size are forwarded to all known subclusters (i.e., subclusters
|
||||||
|
* where we scheduled containers before), as they may represent a user attempt
|
||||||
|
* to cancel a previous request (and we are mostly stateless now, so should
|
||||||
|
* forward to all known RMs).
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Invariants:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The policy always excludes non-active RMs.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The policy always excludes RMs that do not appear in the policy configuration
|
||||||
|
* weights, or have a weight of 0 (even if localized resources explicit refer to
|
||||||
|
* it).
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* (Bar rounding to closest ceiling of fractional containers) The sum of
|
||||||
|
* requests made to multiple RMs at the ANY level "adds-up" to the user request.
|
||||||
|
* The maximum possible excess in a given request is a number of containers less
|
||||||
|
* or equal to number of sub-clusters in the federation.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class LocalityMulticastAMRMProxyPolicy extends AbstractAMRMProxyPolicy {
|
||||||
|
|
||||||
|
public static final Logger LOG =
|
||||||
|
LoggerFactory.getLogger(LocalityMulticastAMRMProxyPolicy.class);
|
||||||
|
|
||||||
|
private Map<SubClusterId, Float> weights;
|
||||||
|
private SubClusterResolver resolver;
|
||||||
|
|
||||||
|
private Map<SubClusterId, Resource> headroom;
|
||||||
|
private float hrAlpha;
|
||||||
|
private FederationStateStoreFacade federationFacade;
|
||||||
|
private AllocationBookkeeper bookkeeper;
|
||||||
|
private SubClusterId homeSubcluster;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reinitialize(
|
||||||
|
FederationPolicyInitializationContext policyContext)
|
||||||
|
throws FederationPolicyInitializationException {
|
||||||
|
|
||||||
|
// save reference to old weights
|
||||||
|
WeightedPolicyInfo tempPolicy = getPolicyInfo();
|
||||||
|
|
||||||
|
super.reinitialize(policyContext);
|
||||||
|
if (!getIsDirty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<SubClusterId, Float> newWeightsConverted = new HashMap<>();
|
||||||
|
boolean allInactive = true;
|
||||||
|
WeightedPolicyInfo policy = getPolicyInfo();
|
||||||
|
if (policy.getAMRMPolicyWeights() == null
|
||||||
|
|| policy.getAMRMPolicyWeights().size() == 0) {
|
||||||
|
allInactive = false;
|
||||||
|
} else {
|
||||||
|
for (Map.Entry<SubClusterIdInfo, Float> e : policy.getAMRMPolicyWeights()
|
||||||
|
.entrySet()) {
|
||||||
|
if (e.getValue() > 0) {
|
||||||
|
allInactive = false;
|
||||||
|
}
|
||||||
|
newWeightsConverted.put(e.getKey().toId(), e.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (allInactive) {
|
||||||
|
// reset the policyInfo and throw
|
||||||
|
setPolicyInfo(tempPolicy);
|
||||||
|
throw new FederationPolicyInitializationException(
|
||||||
|
"The weights used to configure "
|
||||||
|
+ "this policy are all set to zero! (no ResourceRequest could be "
|
||||||
|
+ "forwarded with this setting.)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (policyContext.getHomeSubcluster() == null) {
|
||||||
|
setPolicyInfo(tempPolicy);
|
||||||
|
throw new FederationPolicyInitializationException("The homeSubcluster "
|
||||||
|
+ "filed in the context must be initialized to use this policy");
|
||||||
|
}
|
||||||
|
|
||||||
|
weights = newWeightsConverted;
|
||||||
|
resolver = policyContext.getFederationSubclusterResolver();
|
||||||
|
|
||||||
|
if (headroom == null) {
|
||||||
|
headroom = new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
hrAlpha = policy.getHeadroomAlpha();
|
||||||
|
|
||||||
|
this.federationFacade =
|
||||||
|
policyContext.getFederationStateStoreFacade();
|
||||||
|
this.bookkeeper = new AllocationBookkeeper();
|
||||||
|
this.homeSubcluster = policyContext.getHomeSubcluster();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notifyOfResponse(SubClusterId subClusterId,
|
||||||
|
AllocateResponse response) throws YarnException {
|
||||||
|
// stateless policy does not care about responses except tracking headroom
|
||||||
|
headroom.put(subClusterId, response.getAvailableResources());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<SubClusterId, List<ResourceRequest>> splitResourceRequests(
|
||||||
|
List<ResourceRequest> resourceRequests) throws YarnException {
|
||||||
|
|
||||||
|
// object used to accumulate statistics about the answer, initialize with
|
||||||
|
// active subclusters.
|
||||||
|
bookkeeper.reinitialize(federationFacade.getSubClusters(true));
|
||||||
|
|
||||||
|
List<ResourceRequest> nonLocalizedRequests =
|
||||||
|
new ArrayList<ResourceRequest>();
|
||||||
|
|
||||||
|
SubClusterId targetId = null;
|
||||||
|
Set<SubClusterId> targetIds = null;
|
||||||
|
|
||||||
|
// if the RR is resolved to a local subcluster add it directly (node and
|
||||||
|
// resolvable racks)
|
||||||
|
for (ResourceRequest rr : resourceRequests) {
|
||||||
|
targetId = null;
|
||||||
|
targetIds = null;
|
||||||
|
|
||||||
|
// Handle: ANY (accumulated for later)
|
||||||
|
if (ResourceRequest.isAnyLocation(rr.getResourceName())) {
|
||||||
|
nonLocalizedRequests.add(rr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle "node" requests
|
||||||
|
try {
|
||||||
|
targetId = resolver.getSubClusterForNode(rr.getResourceName());
|
||||||
|
} catch (YarnException e) {
|
||||||
|
// this might happen as we can't differentiate node from rack names
|
||||||
|
// we log altogether later
|
||||||
|
}
|
||||||
|
if (bookkeeper.isActiveAndEnabled(targetId)) {
|
||||||
|
bookkeeper.addLocalizedNodeRR(targetId, rr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle "rack" requests
|
||||||
|
try {
|
||||||
|
targetIds = resolver.getSubClustersForRack(rr.getResourceName());
|
||||||
|
} catch (YarnException e) {
|
||||||
|
// this might happen as we can't differentiate node from rack names
|
||||||
|
// we log altogether later
|
||||||
|
}
|
||||||
|
if (targetIds != null && targetIds.size() > 0) {
|
||||||
|
for (SubClusterId tid : targetIds) {
|
||||||
|
if (bookkeeper.isActiveAndEnabled(tid)) {
|
||||||
|
bookkeeper.addRackRR(tid, rr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle node/rack requests that the SubClusterResolver cannot map to
|
||||||
|
// any cluster. Defaulting to home subcluster.
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("ERROR resolving sub-cluster for resourceName: "
|
||||||
|
+ rr.getResourceName() + " we are falling back to homeSubCluster:"
|
||||||
|
+ homeSubcluster);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If home-subcluster is not active, ignore node/rack request
|
||||||
|
if (bookkeeper.isActiveAndEnabled(homeSubcluster)) {
|
||||||
|
bookkeeper.addLocalizedNodeRR(homeSubcluster, rr);
|
||||||
|
} else {
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("The homeSubCluster (" + homeSubcluster + ") we are "
|
||||||
|
+ "defaulting to is not active, the ResourceRequest "
|
||||||
|
+ "will be ignored.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle all non-localized requests (ANY)
|
||||||
|
splitAnyRequests(nonLocalizedRequests, bookkeeper);
|
||||||
|
|
||||||
|
return bookkeeper.getAnswer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It splits a list of non-localized resource requests among sub-clusters.
|
||||||
|
*/
|
||||||
|
private void splitAnyRequests(List<ResourceRequest> originalResourceRequests,
|
||||||
|
AllocationBookkeeper allocationBookkeeper) throws YarnException {
|
||||||
|
|
||||||
|
for (ResourceRequest resourceRequest : originalResourceRequests) {
|
||||||
|
|
||||||
|
// FIRST: pick the target set of subclusters (based on whether this RR
|
||||||
|
// is associated with other localized requests via an allocationId)
|
||||||
|
Long allocationId = resourceRequest.getAllocationRequestId();
|
||||||
|
Set<SubClusterId> targetSubclusters;
|
||||||
|
if (allocationBookkeeper.getSubClustersForId(allocationId) != null) {
|
||||||
|
targetSubclusters =
|
||||||
|
allocationBookkeeper.getSubClustersForId(allocationId);
|
||||||
|
} else {
|
||||||
|
targetSubclusters = allocationBookkeeper.getActiveAndEnabledSC();
|
||||||
|
}
|
||||||
|
|
||||||
|
// SECOND: pick how much to ask to each RM for each request
|
||||||
|
splitIndividualAny(resourceRequest, targetSubclusters,
|
||||||
|
allocationBookkeeper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a projection of this ANY {@link ResourceRequest} that belongs to
|
||||||
|
* this sub-cluster. This is done based on the "count" of the containers that
|
||||||
|
* require locality in each sublcuster (if any) or based on the "weights" and
|
||||||
|
* headroom.
|
||||||
|
*/
|
||||||
|
private void splitIndividualAny(ResourceRequest originalResourceRequest,
|
||||||
|
Set<SubClusterId> targetSubclusters,
|
||||||
|
AllocationBookkeeper allocationBookkeeper) {
|
||||||
|
|
||||||
|
long allocationId = originalResourceRequest.getAllocationRequestId();
|
||||||
|
|
||||||
|
for (SubClusterId targetId : targetSubclusters) {
|
||||||
|
float numContainer = originalResourceRequest.getNumContainers();
|
||||||
|
|
||||||
|
// If the ANY request has 0 containers to begin with we must forward it to
|
||||||
|
// any RM we have previously contacted (this might be the user way
|
||||||
|
// to cancel a previous request).
|
||||||
|
if (numContainer == 0 && headroom.containsKey(targetId)) {
|
||||||
|
allocationBookkeeper.addAnyRR(targetId, originalResourceRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If ANY is associated with localized asks, split based on their ratio
|
||||||
|
if (allocationBookkeeper.getSubClustersForId(allocationId) != null) {
|
||||||
|
float localityBasedWeight = getLocalityBasedWeighting(allocationId,
|
||||||
|
targetId, allocationBookkeeper);
|
||||||
|
numContainer = numContainer * localityBasedWeight;
|
||||||
|
} else {
|
||||||
|
// split ANY based on load and policy configuration
|
||||||
|
float headroomWeighting =
|
||||||
|
getHeadroomWeighting(targetId, allocationBookkeeper);
|
||||||
|
float policyWeighting =
|
||||||
|
getPolicyConfigWeighting(targetId, allocationBookkeeper);
|
||||||
|
// hrAlpha controls how much headroom influencing decision
|
||||||
|
numContainer = numContainer
|
||||||
|
* (hrAlpha * headroomWeighting + (1 - hrAlpha) * policyWeighting);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the calculated request is non-empty add it to the answer
|
||||||
|
if (numContainer > 0) {
|
||||||
|
ResourceRequest out =
|
||||||
|
ResourceRequest.newInstance(originalResourceRequest.getPriority(),
|
||||||
|
originalResourceRequest.getResourceName(),
|
||||||
|
originalResourceRequest.getCapability(),
|
||||||
|
originalResourceRequest.getNumContainers(),
|
||||||
|
originalResourceRequest.getRelaxLocality(),
|
||||||
|
originalResourceRequest.getNodeLabelExpression(),
|
||||||
|
originalResourceRequest.getExecutionTypeRequest());
|
||||||
|
out.setAllocationRequestId(allocationId);
|
||||||
|
out.setNumContainers((int) Math.ceil(numContainer));
|
||||||
|
if (out.isAnyLocation(out.getResourceName())) {
|
||||||
|
allocationBookkeeper.addAnyRR(targetId, out);
|
||||||
|
} else {
|
||||||
|
allocationBookkeeper.addRackRR(targetId, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the weight to assign to a subcluster based on how many local
|
||||||
|
* requests a subcluster is target of.
|
||||||
|
*/
|
||||||
|
private float getLocalityBasedWeighting(long reqId, SubClusterId targetId,
|
||||||
|
AllocationBookkeeper allocationBookkeeper) {
|
||||||
|
float totWeight = allocationBookkeeper.getTotNumLocalizedContainers();
|
||||||
|
float localWeight =
|
||||||
|
allocationBookkeeper.getNumLocalizedContainers(reqId, targetId);
|
||||||
|
return totWeight > 0 ? localWeight / totWeight : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the "weighting" to give to a sublcuster based on the configured
|
||||||
|
* policy weights (for the active subclusters).
|
||||||
|
*/
|
||||||
|
private float getPolicyConfigWeighting(SubClusterId targetId,
|
||||||
|
AllocationBookkeeper allocationBookkeeper) {
|
||||||
|
float totWeight = allocationBookkeeper.totPolicyWeight;
|
||||||
|
Float localWeight = weights.get(targetId);
|
||||||
|
return (localWeight != null && totWeight > 0) ? localWeight / totWeight : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the weighting based on available headroom. This is proportional to
|
||||||
|
* the available headroom memory announced by RM, or to 1/N for RMs we have
|
||||||
|
* not seen yet. If all RMs report zero headroom, we fallback to 1/N again.
|
||||||
|
*/
|
||||||
|
private float getHeadroomWeighting(SubClusterId targetId,
|
||||||
|
AllocationBookkeeper allocationBookkeeper) {
|
||||||
|
|
||||||
|
// baseline weight for all RMs
|
||||||
|
float headroomWeighting =
|
||||||
|
1 / (float) allocationBookkeeper.getActiveAndEnabledSC().size();
|
||||||
|
|
||||||
|
// if we have headroom infomration for this sub-cluster (and we are safe
|
||||||
|
// from /0 issues)
|
||||||
|
if (headroom.containsKey(targetId)
|
||||||
|
&& allocationBookkeeper.totHeadroomMemory > 0) {
|
||||||
|
// compute which portion of the RMs that are active/enabled have reported
|
||||||
|
// their headroom (needed as adjustment factor)
|
||||||
|
// (note: getActiveAndEnabledSC should never be null/zero)
|
||||||
|
float ratioHeadroomKnown = allocationBookkeeper.totHeadRoomEnabledRMs
|
||||||
|
/ (float) allocationBookkeeper.getActiveAndEnabledSC().size();
|
||||||
|
|
||||||
|
// headroomWeighting is the ratio of headroom memory in the targetId
|
||||||
|
// cluster / total memory. The ratioHeadroomKnown factor is applied to
|
||||||
|
// adjust for missing information and ensure sum of allocated containers
|
||||||
|
// closely approximate what the user asked (small excess).
|
||||||
|
headroomWeighting = (headroom.get(targetId).getMemorySize()
|
||||||
|
/ allocationBookkeeper.totHeadroomMemory) * (ratioHeadroomKnown);
|
||||||
|
}
|
||||||
|
return headroomWeighting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This helper class is used to book-keep the requests made to each
|
||||||
|
* subcluster, and maintain useful statistics to split ANY requests.
|
||||||
|
*/
|
||||||
|
private final class AllocationBookkeeper {
|
||||||
|
|
||||||
|
// the answer being accumulated
|
||||||
|
private Map<SubClusterId, List<ResourceRequest>> answer = new TreeMap<>();
|
||||||
|
|
||||||
|
// stores how many containers we have allocated in each RM for localized
|
||||||
|
// asks, used to correctly "spread" the corresponding ANY
|
||||||
|
private Map<Long, Map<SubClusterId, AtomicLong>> countContainersPerRM =
|
||||||
|
new HashMap<>();
|
||||||
|
|
||||||
|
private Set<SubClusterId> activeAndEnabledSC = new HashSet<>();
|
||||||
|
private long totNumLocalizedContainers = 0;
|
||||||
|
private float totHeadroomMemory = 0;
|
||||||
|
private int totHeadRoomEnabledRMs = 0;
|
||||||
|
private float totPolicyWeight = 0;
|
||||||
|
|
||||||
|
private void reinitialize(
|
||||||
|
Map<SubClusterId, SubClusterInfo> activeSubclusters)
|
||||||
|
throws YarnException {
|
||||||
|
|
||||||
|
// reset data structures
|
||||||
|
answer.clear();
|
||||||
|
countContainersPerRM.clear();
|
||||||
|
activeAndEnabledSC.clear();
|
||||||
|
totNumLocalizedContainers = 0;
|
||||||
|
totHeadroomMemory = 0;
|
||||||
|
totHeadRoomEnabledRMs = 0;
|
||||||
|
totPolicyWeight = 0;
|
||||||
|
|
||||||
|
// pre-compute the set of subclusters that are both active and enabled by
|
||||||
|
// the policy weights, and accumulate their total weight
|
||||||
|
for (Map.Entry<SubClusterId, Float> entry : weights.entrySet()) {
|
||||||
|
if (entry.getValue() > 0
|
||||||
|
&& activeSubclusters.containsKey(entry.getKey())) {
|
||||||
|
activeAndEnabledSC.add(entry.getKey());
|
||||||
|
totPolicyWeight += entry.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeAndEnabledSC.size() < 1) {
|
||||||
|
throw new NoActiveSubclustersException(
|
||||||
|
"None of the subclusters enabled in this policy (weight>0) are "
|
||||||
|
+ "currently active we cannot forward the ResourceRequest(s)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// pre-compute headroom-based weights for active/enabled subclusters
|
||||||
|
for (Map.Entry<SubClusterId, Resource> r : headroom.entrySet()) {
|
||||||
|
if (activeAndEnabledSC.contains(r.getKey())) {
|
||||||
|
totHeadroomMemory += r.getValue().getMemorySize();
|
||||||
|
totHeadRoomEnabledRMs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add to the answer a localized node request, and keeps track of statistics
|
||||||
|
* on a per-allocation-id and per-subcluster bases.
|
||||||
|
*/
|
||||||
|
private void addLocalizedNodeRR(SubClusterId targetId, ResourceRequest rr) {
|
||||||
|
Preconditions.checkArgument(!rr.isAnyLocation(rr.getResourceName()));
|
||||||
|
|
||||||
|
if (!countContainersPerRM.containsKey(rr.getAllocationRequestId())) {
|
||||||
|
countContainersPerRM.put(rr.getAllocationRequestId(), new HashMap<>());
|
||||||
|
}
|
||||||
|
if (!countContainersPerRM.get(rr.getAllocationRequestId())
|
||||||
|
.containsKey(targetId)) {
|
||||||
|
countContainersPerRM.get(rr.getAllocationRequestId()).put(targetId,
|
||||||
|
new AtomicLong(0));
|
||||||
|
}
|
||||||
|
countContainersPerRM.get(rr.getAllocationRequestId()).get(targetId)
|
||||||
|
.addAndGet(rr.getNumContainers());
|
||||||
|
|
||||||
|
totNumLocalizedContainers += rr.getNumContainers();
|
||||||
|
|
||||||
|
internalAddToAnswer(targetId, rr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a rack-local request to the final asnwer.
|
||||||
|
*/
|
||||||
|
public void addRackRR(SubClusterId targetId, ResourceRequest rr) {
|
||||||
|
Preconditions.checkArgument(!rr.isAnyLocation(rr.getResourceName()));
|
||||||
|
internalAddToAnswer(targetId, rr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an ANY request to the final answer.
|
||||||
|
*/
|
||||||
|
private void addAnyRR(SubClusterId targetId, ResourceRequest rr) {
|
||||||
|
Preconditions.checkArgument(rr.isAnyLocation(rr.getResourceName()));
|
||||||
|
internalAddToAnswer(targetId, rr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void internalAddToAnswer(SubClusterId targetId,
|
||||||
|
ResourceRequest partialRR) {
|
||||||
|
if (!answer.containsKey(targetId)) {
|
||||||
|
answer.put(targetId, new ArrayList<ResourceRequest>());
|
||||||
|
}
|
||||||
|
answer.get(targetId).add(partialRR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all known subclusters associated with an allocation id.
|
||||||
|
*
|
||||||
|
* @param allocationId the allocation id considered
|
||||||
|
*
|
||||||
|
* @return the list of {@link SubClusterId}s associated with this allocation
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
private Set<SubClusterId> getSubClustersForId(long allocationId) {
|
||||||
|
if (countContainersPerRM.get(allocationId) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return countContainersPerRM.get(allocationId).keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the answer accumulated so far.
|
||||||
|
*
|
||||||
|
* @return the answer
|
||||||
|
*/
|
||||||
|
private Map<SubClusterId, List<ResourceRequest>> getAnswer() {
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the set of sub-clusters that are both active and allowed by our
|
||||||
|
* policy (weight > 0).
|
||||||
|
*
|
||||||
|
* @return a set of active and enabled {@link SubClusterId}s
|
||||||
|
*/
|
||||||
|
private Set<SubClusterId> getActiveAndEnabledSC() {
|
||||||
|
return activeAndEnabledSC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the total number of container coming from localized requests.
|
||||||
|
*/
|
||||||
|
private long getTotNumLocalizedContainers() {
|
||||||
|
return totNumLocalizedContainers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of containers matching an allocation Id that are
|
||||||
|
* localized in the targetId subcluster.
|
||||||
|
*/
|
||||||
|
private long getNumLocalizedContainers(long allocationId,
|
||||||
|
SubClusterId targetId) {
|
||||||
|
AtomicLong c = countContainersPerRM.get(allocationId).get(targetId);
|
||||||
|
return c == null ? 0 : c.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true is the subcluster request is both active and enabled.
|
||||||
|
*/
|
||||||
|
private boolean isActiveAndEnabled(SubClusterId targetId) {
|
||||||
|
if (targetId == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return getActiveAndEnabledSC().contains(targetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,4 +17,3 @@
|
||||||
*/
|
*/
|
||||||
/** AMRMPRoxy policies. **/
|
/** AMRMPRoxy policies. **/
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.amrmproxy;
|
package org.apache.hadoop.yarn.server.federation.policies.amrmproxy;
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,19 @@
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.dao;
|
package org.apache.hadoop.yarn.server.federation.policies.dao;
|
||||||
|
|
||||||
import com.sun.jersey.api.json.JSONConfiguration;
|
import java.io.StringReader;
|
||||||
import com.sun.jersey.api.json.JSONJAXBContext;
|
import java.io.StringWriter;
|
||||||
import com.sun.jersey.api.json.JSONMarshaller;
|
import java.nio.ByteBuffer;
|
||||||
import com.sun.jersey.api.json.JSONUnmarshaller;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
|
import javax.xml.bind.Marshaller;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.classification.InterfaceStability;
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
|
@ -29,24 +38,16 @@
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.xml.bind.JAXBException;
|
import com.sun.jersey.api.json.JSONConfiguration;
|
||||||
import javax.xml.bind.Marshaller;
|
import com.sun.jersey.api.json.JSONJAXBContext;
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import com.sun.jersey.api.json.JSONMarshaller;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import com.sun.jersey.api.json.JSONUnmarshaller;
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a DAO class for the configuration of parameteres for federation
|
* This is a DAO class for the configuration of parameteres for federation
|
||||||
* policies. This generalizes several possible configurations as two lists of
|
* policies. This generalizes several possible configurations as two lists of
|
||||||
* {@link SubClusterIdInfo} and corresponding weights as a
|
* {@link SubClusterIdInfo} and corresponding weights as a {@link Float}. The
|
||||||
* {@link Float}. The interpretation of the weight is left to the logic in
|
* interpretation of the weight is left to the logic in the policy.
|
||||||
* the policy.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@InterfaceAudience.Private
|
@InterfaceAudience.Private
|
||||||
|
@ -57,12 +58,14 @@ public class WeightedPolicyInfo {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
LoggerFactory.getLogger(WeightedPolicyInfo.class);
|
LoggerFactory.getLogger(WeightedPolicyInfo.class);
|
||||||
|
private static JSONJAXBContext jsonjaxbContext = initContext();
|
||||||
private Map<SubClusterIdInfo, Float> routerPolicyWeights = new HashMap<>();
|
private Map<SubClusterIdInfo, Float> routerPolicyWeights = new HashMap<>();
|
||||||
private Map<SubClusterIdInfo, Float> amrmPolicyWeights = new HashMap<>();
|
private Map<SubClusterIdInfo, Float> amrmPolicyWeights = new HashMap<>();
|
||||||
private float headroomAlpha;
|
private float headroomAlpha;
|
||||||
|
|
||||||
private static JSONJAXBContext jsonjaxbContext = initContext();
|
public WeightedPolicyInfo() {
|
||||||
|
// JAXB needs this
|
||||||
|
}
|
||||||
|
|
||||||
private static JSONJAXBContext initContext() {
|
private static JSONJAXBContext initContext() {
|
||||||
try {
|
try {
|
||||||
|
@ -74,8 +77,46 @@ private static JSONJAXBContext initContext() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WeightedPolicyInfo() {
|
/**
|
||||||
//JAXB needs this
|
* Deserializes a {@link WeightedPolicyInfo} from a byte UTF-8 JSON
|
||||||
|
* representation.
|
||||||
|
*
|
||||||
|
* @param bb the input byte representation.
|
||||||
|
*
|
||||||
|
* @return the {@link WeightedPolicyInfo} represented.
|
||||||
|
*
|
||||||
|
* @throws FederationPolicyInitializationException if a deserializaiton error
|
||||||
|
* occurs.
|
||||||
|
*/
|
||||||
|
public static WeightedPolicyInfo fromByteBuffer(ByteBuffer bb)
|
||||||
|
throws FederationPolicyInitializationException {
|
||||||
|
|
||||||
|
if (jsonjaxbContext == null) {
|
||||||
|
throw new FederationPolicyInitializationException(
|
||||||
|
"JSONJAXBContext should" + " not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONUnmarshaller unmarshaller = jsonjaxbContext.createJSONUnmarshaller();
|
||||||
|
final byte[] bytes = new byte[bb.remaining()];
|
||||||
|
bb.get(bytes);
|
||||||
|
String params = new String(bytes, Charset.forName("UTF-8"));
|
||||||
|
|
||||||
|
WeightedPolicyInfo weightedPolicyInfo = unmarshaller.unmarshalFromJSON(
|
||||||
|
new StringReader(params), WeightedPolicyInfo.class);
|
||||||
|
return weightedPolicyInfo;
|
||||||
|
} catch (JAXBException j) {
|
||||||
|
throw new FederationPolicyInitializationException(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter of the router weights.
|
||||||
|
*
|
||||||
|
* @return the router weights.
|
||||||
|
*/
|
||||||
|
public Map<SubClusterIdInfo, Float> getRouterPolicyWeights() {
|
||||||
|
return routerPolicyWeights;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,26 +129,9 @@ public void setRouterPolicyWeights(
|
||||||
this.routerPolicyWeights = policyWeights;
|
this.routerPolicyWeights = policyWeights;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Setter method for ARMRMProxy weights.
|
|
||||||
*
|
|
||||||
* @param policyWeights the amrmproxy weights.
|
|
||||||
*/
|
|
||||||
public void setAMRMPolicyWeights(
|
|
||||||
Map<SubClusterIdInfo, Float> policyWeights) {
|
|
||||||
this.amrmPolicyWeights = policyWeights;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter of the router weights.
|
|
||||||
* @return the router weights.
|
|
||||||
*/
|
|
||||||
public Map<SubClusterIdInfo, Float> getRouterPolicyWeights() {
|
|
||||||
return routerPolicyWeights;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for AMRMProxy weights.
|
* Getter for AMRMProxy weights.
|
||||||
|
*
|
||||||
* @return the AMRMProxy weights.
|
* @return the AMRMProxy weights.
|
||||||
*/
|
*/
|
||||||
public Map<SubClusterIdInfo, Float> getAMRMPolicyWeights() {
|
public Map<SubClusterIdInfo, Float> getAMRMPolicyWeights() {
|
||||||
|
@ -115,53 +139,28 @@ public Map<SubClusterIdInfo, Float> getAMRMPolicyWeights() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserializes a {@link WeightedPolicyInfo} from a byte UTF-8 JSON
|
* Setter method for ARMRMProxy weights.
|
||||||
* representation.
|
|
||||||
*
|
*
|
||||||
* @param bb the input byte representation.
|
* @param policyWeights the amrmproxy weights.
|
||||||
*
|
|
||||||
* @return the {@link WeightedPolicyInfo} represented.
|
|
||||||
*
|
|
||||||
* @throws FederationPolicyInitializationException if a deserializaiton error
|
|
||||||
* occurs.
|
|
||||||
*/
|
*/
|
||||||
public static WeightedPolicyInfo fromByteBuffer(ByteBuffer bb)
|
public void setAMRMPolicyWeights(Map<SubClusterIdInfo, Float> policyWeights) {
|
||||||
throws FederationPolicyInitializationException {
|
this.amrmPolicyWeights = policyWeights;
|
||||||
|
|
||||||
if (jsonjaxbContext == null) {
|
|
||||||
throw new FederationPolicyInitializationException("JSONJAXBContext should"
|
|
||||||
+ " not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
JSONUnmarshaller unmarshaller = jsonjaxbContext.createJSONUnmarshaller();
|
|
||||||
final byte[] bytes = new byte[bb.remaining()];
|
|
||||||
bb.get(bytes);
|
|
||||||
String params = new String(bytes, Charset.forName("UTF-8"));
|
|
||||||
|
|
||||||
WeightedPolicyInfo weightedPolicyInfo = unmarshaller
|
|
||||||
.unmarshalFromJSON(new StringReader(params),
|
|
||||||
WeightedPolicyInfo.class);
|
|
||||||
return weightedPolicyInfo;
|
|
||||||
} catch (JAXBException j) {
|
|
||||||
throw new FederationPolicyInitializationException(j);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the policy into a byte array representation in the input {@link
|
* Converts the policy into a byte array representation in the input
|
||||||
* ByteBuffer}.
|
* {@link ByteBuffer}.
|
||||||
*
|
*
|
||||||
* @return byte array representation of this policy configuration.
|
* @return byte array representation of this policy configuration.
|
||||||
*
|
*
|
||||||
* @throws FederationPolicyInitializationException if a serialization error
|
* @throws FederationPolicyInitializationException if a serialization error
|
||||||
* occurs.
|
* occurs.
|
||||||
*/
|
*/
|
||||||
public ByteBuffer toByteBuffer()
|
public ByteBuffer toByteBuffer()
|
||||||
throws FederationPolicyInitializationException {
|
throws FederationPolicyInitializationException {
|
||||||
if (jsonjaxbContext == null) {
|
if (jsonjaxbContext == null) {
|
||||||
throw new FederationPolicyInitializationException("JSONJAXBContext should"
|
throw new FederationPolicyInitializationException(
|
||||||
+ " not be null.");
|
"JSONJAXBContext should" + " not be null.");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String s = toJSONString();
|
String s = toJSONString();
|
||||||
|
@ -186,22 +185,21 @@ public boolean equals(Object other) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
WeightedPolicyInfo otherPolicy =
|
WeightedPolicyInfo otherPolicy = (WeightedPolicyInfo) other;
|
||||||
(WeightedPolicyInfo) other;
|
|
||||||
Map<SubClusterIdInfo, Float> otherAMRMWeights =
|
Map<SubClusterIdInfo, Float> otherAMRMWeights =
|
||||||
otherPolicy.getAMRMPolicyWeights();
|
otherPolicy.getAMRMPolicyWeights();
|
||||||
Map<SubClusterIdInfo, Float> otherRouterWeights =
|
Map<SubClusterIdInfo, Float> otherRouterWeights =
|
||||||
otherPolicy.getRouterPolicyWeights();
|
otherPolicy.getRouterPolicyWeights();
|
||||||
|
|
||||||
boolean amrmWeightsMatch = otherAMRMWeights != null &&
|
boolean amrmWeightsMatch =
|
||||||
getAMRMPolicyWeights() != null &&
|
otherAMRMWeights != null && getAMRMPolicyWeights() != null
|
||||||
CollectionUtils.isEqualCollection(otherAMRMWeights.entrySet(),
|
&& CollectionUtils.isEqualCollection(otherAMRMWeights.entrySet(),
|
||||||
getAMRMPolicyWeights().entrySet());
|
getAMRMPolicyWeights().entrySet());
|
||||||
|
|
||||||
boolean routerWeightsMatch = otherRouterWeights != null &&
|
boolean routerWeightsMatch =
|
||||||
getRouterPolicyWeights() != null &&
|
otherRouterWeights != null && getRouterPolicyWeights() != null
|
||||||
CollectionUtils.isEqualCollection(otherRouterWeights.entrySet(),
|
&& CollectionUtils.isEqualCollection(otherRouterWeights.entrySet(),
|
||||||
getRouterPolicyWeights().entrySet());
|
getRouterPolicyWeights().entrySet());
|
||||||
|
|
||||||
return amrmWeightsMatch && routerWeightsMatch;
|
return amrmWeightsMatch && routerWeightsMatch;
|
||||||
}
|
}
|
||||||
|
@ -215,10 +213,10 @@ public int hashCode() {
|
||||||
* Return the parameter headroomAlpha, used by policies that balance
|
* Return the parameter headroomAlpha, used by policies that balance
|
||||||
* weight-based and load-based considerations in their decisions.
|
* weight-based and load-based considerations in their decisions.
|
||||||
*
|
*
|
||||||
* For policies that use this parameter, values close to 1 indicate that
|
* For policies that use this parameter, values close to 1 indicate that most
|
||||||
* most of the decision should be based on currently observed headroom from
|
* of the decision should be based on currently observed headroom from various
|
||||||
* various sub-clusters, values close to zero, indicate that the decision
|
* sub-clusters, values close to zero, indicate that the decision should be
|
||||||
* should be mostly based on weights and practically ignore current load.
|
* mostly based on weights and practically ignore current load.
|
||||||
*
|
*
|
||||||
* @return the value of headroomAlpha.
|
* @return the value of headroomAlpha.
|
||||||
*/
|
*/
|
||||||
|
@ -227,13 +225,13 @@ public float getHeadroomAlpha() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the parameter headroomAlpha, used by policies that balance
|
* Set the parameter headroomAlpha, used by policies that balance weight-based
|
||||||
* weight-based and load-based considerations in their decisions.
|
* and load-based considerations in their decisions.
|
||||||
*
|
*
|
||||||
* For policies that use this parameter, values close to 1 indicate that
|
* For policies that use this parameter, values close to 1 indicate that most
|
||||||
* most of the decision should be based on currently observed headroom from
|
* of the decision should be based on currently observed headroom from various
|
||||||
* various sub-clusters, values close to zero, indicate that the decision
|
* sub-clusters, values close to zero, indicate that the decision should be
|
||||||
* should be mostly based on weights and practically ignore current load.
|
* mostly based on weights and practically ignore current load.
|
||||||
*
|
*
|
||||||
* @param headroomAlpha the value to use for balancing.
|
* @param headroomAlpha the value to use for balancing.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,4 +17,3 @@
|
||||||
*/
|
*/
|
||||||
/** DAO objects for serializing/deserializing policy configurations. **/
|
/** DAO objects for serializing/deserializing policy configurations. **/
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.dao;
|
package org.apache.hadoop.yarn.server.federation.policies.dao;
|
||||||
|
|
||||||
|
|
|
@ -17,4 +17,3 @@
|
||||||
*/
|
*/
|
||||||
/** Exceptions for policies. **/
|
/** Exceptions for policies. **/
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.exceptions;
|
package org.apache.hadoop.yarn.server.federation.policies.exceptions;
|
||||||
|
|
||||||
|
|
|
@ -17,4 +17,3 @@
|
||||||
*/
|
*/
|
||||||
/** Federation Policies. **/
|
/** Federation Policies. **/
|
||||||
package org.apache.hadoop.yarn.server.federation.policies;
|
package org.apache.hadoop.yarn.server.federation.policies;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.yarn.server.federation.policies.router;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.AbstractConfigurableFederationPolicy;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyInitializationException;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterIdInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base abstract class for {@link FederationRouterPolicy} implementations, that
|
||||||
|
* provides common validation for reinitialization.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractRouterPolicy extends
|
||||||
|
AbstractConfigurableFederationPolicy implements FederationRouterPolicy {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(WeightedPolicyInfo newPolicyInfo)
|
||||||
|
throws FederationPolicyInitializationException {
|
||||||
|
super.validate(newPolicyInfo);
|
||||||
|
Map<SubClusterIdInfo, Float> newWeights =
|
||||||
|
newPolicyInfo.getRouterPolicyWeights();
|
||||||
|
if (newWeights == null || newWeights.size() < 1) {
|
||||||
|
throw new FederationPolicyInitializationException(
|
||||||
|
"Weight vector cannot be null/empty.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -35,11 +35,10 @@ public interface FederationRouterPolicy extends ConfigurableFederationPolicy {
|
||||||
* @param appSubmissionContext the context for the app being submitted.
|
* @param appSubmissionContext the context for the app being submitted.
|
||||||
*
|
*
|
||||||
* @return the sub-cluster as identified by {@link SubClusterId} to route the
|
* @return the sub-cluster as identified by {@link SubClusterId} to route the
|
||||||
* request to.
|
* request to.
|
||||||
*
|
*
|
||||||
* @throws YarnException if the policy cannot determine a viable subcluster.
|
* @throws YarnException if the policy cannot determine a viable subcluster.
|
||||||
*/
|
*/
|
||||||
SubClusterId getHomeSubcluster(
|
SubClusterId getHomeSubcluster(
|
||||||
ApplicationSubmissionContext appSubmissionContext)
|
ApplicationSubmissionContext appSubmissionContext) throws YarnException;
|
||||||
throws YarnException;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.router;
|
package org.apache.hadoop.yarn.server.federation.policies.router;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import java.util.Map;
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.FederationPolicyInitializationContext;
|
import org.apache.hadoop.yarn.server.federation.policies.FederationPolicyInitializationContext;
|
||||||
|
@ -30,34 +30,27 @@
|
||||||
import org.codehaus.jettison.json.JSONException;
|
import org.codehaus.jettison.json.JSONException;
|
||||||
import org.codehaus.jettison.json.JSONObject;
|
import org.codehaus.jettison.json.JSONObject;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This implements a simple load-balancing policy. The policy "weights" are
|
* This implements a simple load-balancing policy. The policy "weights" are
|
||||||
* binary 0/1 values that enable/disable each sub-cluster, and the policy peaks
|
* binary 0/1 values that enable/disable each sub-cluster, and the policy peaks
|
||||||
* the sub-cluster with the least load to forward this application.
|
* the sub-cluster with the least load to forward this application.
|
||||||
*/
|
*/
|
||||||
public class LoadBasedRouterPolicy
|
public class LoadBasedRouterPolicy extends AbstractRouterPolicy {
|
||||||
extends BaseWeightedRouterPolicy {
|
|
||||||
|
|
||||||
private static final Log LOG =
|
|
||||||
LogFactory.getLog(LoadBasedRouterPolicy.class);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reinitialize(FederationPolicyInitializationContext
|
public void reinitialize(FederationPolicyInitializationContext policyContext)
|
||||||
federationPolicyContext)
|
|
||||||
throws FederationPolicyInitializationException {
|
throws FederationPolicyInitializationException {
|
||||||
|
|
||||||
// remember old policyInfo
|
// remember old policyInfo
|
||||||
WeightedPolicyInfo tempPolicy = getPolicyInfo();
|
WeightedPolicyInfo tempPolicy = getPolicyInfo();
|
||||||
|
|
||||||
//attempt new initialization
|
// attempt new initialization
|
||||||
super.reinitialize(federationPolicyContext);
|
super.reinitialize(policyContext);
|
||||||
|
|
||||||
//check extra constraints
|
// check extra constraints
|
||||||
for (Float weight : getPolicyInfo().getRouterPolicyWeights().values()) {
|
for (Float weight : getPolicyInfo().getRouterPolicyWeights().values()) {
|
||||||
if (weight != 0 && weight != 1) {
|
if (weight != 0 && weight != 1) {
|
||||||
//reset to old policyInfo if check fails
|
// reset to old policyInfo if check fails
|
||||||
setPolicyInfo(tempPolicy);
|
setPolicyInfo(tempPolicy);
|
||||||
throw new FederationPolicyInitializationException(
|
throw new FederationPolicyInitializationException(
|
||||||
this.getClass().getCanonicalName()
|
this.getClass().getCanonicalName()
|
||||||
|
@ -69,18 +62,16 @@ public void reinitialize(FederationPolicyInitializationContext
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubClusterId getHomeSubcluster(
|
public SubClusterId getHomeSubcluster(
|
||||||
ApplicationSubmissionContext appSubmissionContext)
|
ApplicationSubmissionContext appSubmissionContext) throws YarnException {
|
||||||
throws YarnException {
|
|
||||||
|
|
||||||
Map<SubClusterId, SubClusterInfo> activeSubclusters =
|
Map<SubClusterId, SubClusterInfo> activeSubclusters =
|
||||||
getActiveSubclusters();
|
getActiveSubclusters();
|
||||||
|
|
||||||
Map<SubClusterIdInfo, Float> weights = getPolicyInfo()
|
Map<SubClusterIdInfo, Float> weights =
|
||||||
.getRouterPolicyWeights();
|
getPolicyInfo().getRouterPolicyWeights();
|
||||||
SubClusterIdInfo chosen = null;
|
SubClusterIdInfo chosen = null;
|
||||||
long currBestMem = -1;
|
long currBestMem = -1;
|
||||||
for (Map.Entry<SubClusterId, SubClusterInfo> entry :
|
for (Map.Entry<SubClusterId, SubClusterInfo> entry : activeSubclusters
|
||||||
activeSubclusters
|
|
||||||
.entrySet()) {
|
.entrySet()) {
|
||||||
SubClusterIdInfo id = new SubClusterIdInfo(entry.getKey());
|
SubClusterIdInfo id = new SubClusterIdInfo(entry.getKey());
|
||||||
if (weights.containsKey(id) && weights.get(id) > 0) {
|
if (weights.containsKey(id) && weights.get(id) > 0) {
|
||||||
|
@ -95,8 +86,7 @@ public SubClusterId getHomeSubcluster(
|
||||||
return chosen.toId();
|
return chosen.toId();
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getAvailableMemory(SubClusterInfo value)
|
private long getAvailableMemory(SubClusterInfo value) throws YarnException {
|
||||||
throws YarnException {
|
|
||||||
try {
|
try {
|
||||||
long mem = -1;
|
long mem = -1;
|
||||||
JSONObject obj = new JSONObject(value.getCapability());
|
JSONObject obj = new JSONObject(value.getCapability());
|
||||||
|
|
|
@ -17,39 +17,32 @@
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.router;
|
package org.apache.hadoop.yarn.server.federation.policies.router;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import java.util.Map;
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterIdInfo;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterIdInfo;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This implements a policy that interprets "weights" as a ordered list of
|
* This implements a policy that interprets "weights" as a ordered list of
|
||||||
* preferences among sub-clusters. Highest weight among active subclusters is
|
* preferences among sub-clusters. Highest weight among active subclusters is
|
||||||
* chosen.
|
* chosen.
|
||||||
*/
|
*/
|
||||||
public class PriorityRouterPolicy
|
public class PriorityRouterPolicy extends AbstractRouterPolicy {
|
||||||
extends BaseWeightedRouterPolicy {
|
|
||||||
|
|
||||||
private static final Log LOG =
|
|
||||||
LogFactory.getLog(PriorityRouterPolicy.class);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubClusterId getHomeSubcluster(
|
public SubClusterId getHomeSubcluster(
|
||||||
ApplicationSubmissionContext appSubmissionContext)
|
ApplicationSubmissionContext appSubmissionContext) throws YarnException {
|
||||||
throws YarnException {
|
|
||||||
|
|
||||||
Map<SubClusterId, SubClusterInfo> activeSubclusters =
|
Map<SubClusterId, SubClusterInfo> activeSubclusters =
|
||||||
getActiveSubclusters();
|
getActiveSubclusters();
|
||||||
|
|
||||||
// This finds the sub-cluster with the highest weight among the
|
// This finds the sub-cluster with the highest weight among the
|
||||||
// currently active ones.
|
// currently active ones.
|
||||||
Map<SubClusterIdInfo, Float> weights = getPolicyInfo()
|
Map<SubClusterIdInfo, Float> weights =
|
||||||
.getRouterPolicyWeights();
|
getPolicyInfo().getRouterPolicyWeights();
|
||||||
SubClusterId chosen = null;
|
SubClusterId chosen = null;
|
||||||
Float currentBest = Float.MIN_VALUE;
|
Float currentBest = Float.MIN_VALUE;
|
||||||
for (SubClusterId id : activeSubclusters.keySet()) {
|
for (SubClusterId id : activeSubclusters.keySet()) {
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.router;
|
package org.apache.hadoop.yarn.server.federation.policies.router;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.FederationPolicyInitializationContext;
|
import org.apache.hadoop.yarn.server.federation.policies.FederationPolicyInitializationContext;
|
||||||
|
@ -25,11 +30,6 @@
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This simple policy picks at uniform random among any of the currently active
|
* This simple policy picks at uniform random among any of the currently active
|
||||||
* subclusters. This policy is easy to use and good for testing.
|
* subclusters. This policy is easy to use and good for testing.
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
* of the "weights", in which case the {@link UniformRandomRouterPolicy} send
|
* of the "weights", in which case the {@link UniformRandomRouterPolicy} send
|
||||||
* load to them, while {@code WeightedRandomRouterPolicy} does not.
|
* load to them, while {@code WeightedRandomRouterPolicy} does not.
|
||||||
*/
|
*/
|
||||||
public class UniformRandomRouterPolicy extends BaseWeightedRouterPolicy {
|
public class UniformRandomRouterPolicy extends AbstractRouterPolicy {
|
||||||
|
|
||||||
private Random rand;
|
private Random rand;
|
||||||
|
|
||||||
|
@ -49,14 +49,14 @@ public UniformRandomRouterPolicy() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reinitialize(
|
public void reinitialize(
|
||||||
FederationPolicyInitializationContext federationPolicyContext)
|
FederationPolicyInitializationContext policyContext)
|
||||||
throws FederationPolicyInitializationException {
|
throws FederationPolicyInitializationException {
|
||||||
FederationPolicyInitializationContextValidator
|
FederationPolicyInitializationContextValidator
|
||||||
.validate(federationPolicyContext, this.getClass().getCanonicalName());
|
.validate(policyContext, this.getClass().getCanonicalName());
|
||||||
|
|
||||||
//note: this overrides BaseWeighterRouterPolicy and ignores the weights
|
// note: this overrides AbstractRouterPolicy and ignores the weights
|
||||||
|
|
||||||
setPolicyContext(federationPolicyContext);
|
setPolicyContext(policyContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,21 +64,19 @@ public void reinitialize(
|
||||||
* depend on the weights in the policy).
|
* depend on the weights in the policy).
|
||||||
*
|
*
|
||||||
* @param appSubmissionContext the context for the app being submitted
|
* @param appSubmissionContext the context for the app being submitted
|
||||||
* (ignored).
|
* (ignored).
|
||||||
*
|
*
|
||||||
* @return a randomly chosen subcluster.
|
* @return a randomly chosen subcluster.
|
||||||
*
|
*
|
||||||
* @throws YarnException if there are no active subclusters.
|
* @throws YarnException if there are no active subclusters.
|
||||||
*/
|
*/
|
||||||
public SubClusterId getHomeSubcluster(
|
public SubClusterId getHomeSubcluster(
|
||||||
ApplicationSubmissionContext appSubmissionContext)
|
ApplicationSubmissionContext appSubmissionContext) throws YarnException {
|
||||||
throws YarnException {
|
|
||||||
|
|
||||||
Map<SubClusterId, SubClusterInfo> activeSubclusters =
|
Map<SubClusterId, SubClusterInfo> activeSubclusters =
|
||||||
getActiveSubclusters();
|
getActiveSubclusters();
|
||||||
|
|
||||||
List<SubClusterId> list =
|
List<SubClusterId> list = new ArrayList<>(activeSubclusters.keySet());
|
||||||
new ArrayList<>(activeSubclusters.keySet());
|
|
||||||
return list.get(rand.nextInt(list.size()));
|
return list.get(rand.nextInt(list.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,32 +18,30 @@
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.router;
|
package org.apache.hadoop.yarn.server.federation.policies.router;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import java.util.Map;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterIdInfo;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterIdInfo;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
||||||
|
import org.slf4j.Logger;
|
||||||
import java.util.Map;
|
import org.slf4j.LoggerFactory;
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This policy implements a weighted random sample among currently active
|
* This policy implements a weighted random sample among currently active
|
||||||
* sub-clusters.
|
* sub-clusters.
|
||||||
*/
|
*/
|
||||||
public class WeightedRandomRouterPolicy
|
public class WeightedRandomRouterPolicy extends AbstractRouterPolicy {
|
||||||
extends BaseWeightedRouterPolicy {
|
|
||||||
|
|
||||||
private static final Log LOG =
|
private static final Logger LOG =
|
||||||
LogFactory.getLog(WeightedRandomRouterPolicy.class);
|
LoggerFactory.getLogger(WeightedRandomRouterPolicy.class);
|
||||||
private Random rand = new Random(System.currentTimeMillis());
|
private Random rand = new Random(System.currentTimeMillis());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubClusterId getHomeSubcluster(
|
public SubClusterId getHomeSubcluster(
|
||||||
ApplicationSubmissionContext appSubmissionContext)
|
ApplicationSubmissionContext appSubmissionContext) throws YarnException {
|
||||||
throws YarnException {
|
|
||||||
|
|
||||||
Map<SubClusterId, SubClusterInfo> activeSubclusters =
|
Map<SubClusterId, SubClusterInfo> activeSubclusters =
|
||||||
getActiveSubclusters();
|
getActiveSubclusters();
|
||||||
|
@ -52,13 +50,13 @@ public SubClusterId getHomeSubcluster(
|
||||||
// changes dynamically (and this would unfairly spread the load to
|
// changes dynamically (and this would unfairly spread the load to
|
||||||
// sub-clusters adjacent to an inactive one), hence we need to count/scan
|
// sub-clusters adjacent to an inactive one), hence we need to count/scan
|
||||||
// the list and based on weight pick the next sub-cluster.
|
// the list and based on weight pick the next sub-cluster.
|
||||||
Map<SubClusterIdInfo, Float> weights = getPolicyInfo()
|
Map<SubClusterIdInfo, Float> weights =
|
||||||
.getRouterPolicyWeights();
|
getPolicyInfo().getRouterPolicyWeights();
|
||||||
|
|
||||||
float totActiveWeight = 0;
|
float totActiveWeight = 0;
|
||||||
for(Map.Entry<SubClusterIdInfo, Float> entry : weights.entrySet()){
|
for (Map.Entry<SubClusterIdInfo, Float> entry : weights.entrySet()) {
|
||||||
if(entry.getKey()!=null && activeSubclusters.containsKey(entry.getKey()
|
if (entry.getKey() != null
|
||||||
.toId())){
|
&& activeSubclusters.containsKey(entry.getKey().toId())) {
|
||||||
totActiveWeight += entry.getValue();
|
totActiveWeight += entry.getValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +71,7 @@ public SubClusterId getHomeSubcluster(
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//should never happen
|
// should never happen
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,4 +17,3 @@
|
||||||
*/
|
*/
|
||||||
/** Router policies. **/
|
/** Router policies. **/
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.router;
|
package org.apache.hadoop.yarn.server.federation.policies.router;
|
||||||
|
|
||||||
|
|
|
@ -57,11 +57,11 @@ public Set<SubClusterId> getSubClustersForRack(String rackname)
|
||||||
return rackToSubClusters.get(rackname);
|
return rackToSubClusters.get(rackname);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, SubClusterId> getNodeToSubCluster() {
|
public Map<String, SubClusterId> getNodeToSubCluster() {
|
||||||
return nodeToSubCluster;
|
return nodeToSubCluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, Set<SubClusterId>> getRackToSubClusters() {
|
public Map<String, Set<SubClusterId>> getRackToSubClusters() {
|
||||||
return rackToSubClusters;
|
return rackToSubClusters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,14 +22,17 @@
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
||||||
|
import org.apache.hadoop.yarn.api.records.ResourceRequest;
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.amrmproxy.FederationAMRMProxyPolicy;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyInitializationException;
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyInitializationException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.exceptions.NoActiveSubclustersException;
|
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.router.FederationRouterPolicy;
|
import org.apache.hadoop.yarn.server.federation.policies.router.FederationRouterPolicy;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
||||||
|
@ -49,6 +52,7 @@ public abstract class BaseFederationPoliciesTest {
|
||||||
private ApplicationSubmissionContext applicationSubmissionContext =
|
private ApplicationSubmissionContext applicationSubmissionContext =
|
||||||
mock(ApplicationSubmissionContext.class);
|
mock(ApplicationSubmissionContext.class);
|
||||||
private Random rand = new Random();
|
private Random rand = new Random();
|
||||||
|
private SubClusterId homeSubCluster;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReinitilialize() throws YarnException {
|
public void testReinitilialize() throws YarnException {
|
||||||
|
@ -88,16 +92,22 @@ public void testReinitilializeBad3() throws YarnException {
|
||||||
getPolicy().reinitialize(fpc);
|
getPolicy().reinitialize(fpc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = NoActiveSubclustersException.class)
|
@Test(expected = FederationPolicyException.class)
|
||||||
public void testNoSubclusters() throws YarnException {
|
public void testNoSubclusters() throws YarnException {
|
||||||
// empty the activeSubclusters map
|
// empty the activeSubclusters map
|
||||||
FederationPoliciesTestUtil.initializePolicyContext(getPolicy(),
|
FederationPoliciesTestUtil.initializePolicyContext(getPolicy(),
|
||||||
getPolicyInfo(), new HashMap<>());
|
getPolicyInfo(), new HashMap<>());
|
||||||
|
|
||||||
ConfigurableFederationPolicy currentPolicy = getPolicy();
|
ConfigurableFederationPolicy localPolicy = getPolicy();
|
||||||
if (currentPolicy instanceof FederationRouterPolicy) {
|
if (localPolicy instanceof FederationRouterPolicy) {
|
||||||
((FederationRouterPolicy) currentPolicy)
|
((FederationRouterPolicy) localPolicy)
|
||||||
.getHomeSubcluster(getApplicationSubmissionContext());
|
.getHomeSubcluster(getApplicationSubmissionContext());
|
||||||
|
} else {
|
||||||
|
String[] hosts = new String[] {"host1", "host2" };
|
||||||
|
List<ResourceRequest> resourceRequests = FederationPoliciesTestUtil
|
||||||
|
.createResourceRequests(hosts, 2 * 1024, 2, 1, 3, null, false);
|
||||||
|
((FederationAMRMProxyPolicy) localPolicy)
|
||||||
|
.splitResourceRequests(resourceRequests);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,4 +162,12 @@ public void setRand(Random rand) {
|
||||||
this.rand = rand;
|
this.rand = rand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SubClusterId getHomeSubCluster() {
|
||||||
|
return homeSubCluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHomeSubCluster(SubClusterId homeSubCluster) {
|
||||||
|
this.homeSubCluster = homeSubCluster;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,22 +16,20 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.federation.policies;
|
package org.apache.hadoop.yarn.server.federation.policies;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.amrmproxy.FederationAMRMProxyPolicy;
|
import org.apache.hadoop.yarn.server.federation.policies.amrmproxy.FederationAMRMProxyPolicy;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyInitializationException;
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyInitializationException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.router.FederationRouterPolicy;
|
import org.apache.hadoop.yarn.server.federation.policies.router.FederationRouterPolicy;
|
||||||
import org.apache.hadoop.yarn.server.federation.resolver.SubClusterResolver;
|
import org.apache.hadoop.yarn.server.federation.resolver.SubClusterResolver;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterPolicyConfiguration;
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterPolicyConfiguration;
|
||||||
|
|
||||||
import org.apache.hadoop.yarn.server.federation.utils.FederationPoliciesTestUtil;
|
import org.apache.hadoop.yarn.server.federation.utils.FederationPoliciesTestUtil;
|
||||||
import org.apache.hadoop.yarn.server.federation.utils.FederationStateStoreFacade;
|
import org.apache.hadoop.yarn.server.federation.utils.FederationStateStoreFacade;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class for {@link FederationPolicyInitializationContextValidator}.
|
* Test class for {@link FederationPolicyInitializationContextValidator}.
|
||||||
*/
|
*/
|
||||||
|
@ -45,11 +43,10 @@ public class TestFederationPolicyInitializationContextValidator {
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
goodFacade = FederationPoliciesTestUtil.initFacade();
|
goodFacade = FederationPoliciesTestUtil.initFacade();
|
||||||
goodConfig =
|
goodConfig = new MockPolicyManager().serializeConf();
|
||||||
new MockPolicyManager().serializeConf();
|
goodSR = FederationPoliciesTestUtil.initResolver();
|
||||||
goodSR =FederationPoliciesTestUtil.initResolver();
|
context = new FederationPolicyInitializationContext(goodConfig, goodSR,
|
||||||
context = new
|
goodFacade);
|
||||||
FederationPolicyInitializationContext(goodConfig, goodSR, goodFacade);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -100,8 +97,7 @@ private class MockPolicyManager implements FederationPolicyManager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FederationAMRMProxyPolicy getAMRMPolicy(
|
public FederationAMRMProxyPolicy getAMRMPolicy(
|
||||||
FederationPolicyInitializationContext
|
FederationPolicyInitializationContext policyContext,
|
||||||
federationPolicyInitializationContext,
|
|
||||||
FederationAMRMProxyPolicy oldInstance)
|
FederationAMRMProxyPolicy oldInstance)
|
||||||
throws FederationPolicyInitializationException {
|
throws FederationPolicyInitializationException {
|
||||||
return null;
|
return null;
|
||||||
|
@ -109,8 +105,7 @@ public FederationAMRMProxyPolicy getAMRMPolicy(
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FederationRouterPolicy getRouterPolicy(
|
public FederationRouterPolicy getRouterPolicy(
|
||||||
FederationPolicyInitializationContext
|
FederationPolicyInitializationContext policyContext,
|
||||||
federationPolicyInitializationContext,
|
|
||||||
FederationRouterPolicy oldInstance)
|
FederationRouterPolicy oldInstance)
|
||||||
throws FederationPolicyInitializationException {
|
throws FederationPolicyInitializationException {
|
||||||
return null;
|
return null;
|
||||||
|
@ -120,8 +115,8 @@ public FederationRouterPolicy getRouterPolicy(
|
||||||
public SubClusterPolicyConfiguration serializeConf()
|
public SubClusterPolicyConfiguration serializeConf()
|
||||||
throws FederationPolicyInitializationException {
|
throws FederationPolicyInitializationException {
|
||||||
ByteBuffer buf = ByteBuffer.allocate(0);
|
ByteBuffer buf = ByteBuffer.allocate(0);
|
||||||
return SubClusterPolicyConfiguration
|
return SubClusterPolicyConfiguration.newInstance("queue1",
|
||||||
.newInstance("queue1", this.getClass().getCanonicalName(), buf);
|
this.getClass().getCanonicalName(), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.yarn.server.federation.policies.amrmproxy;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
|
||||||
|
import org.apache.hadoop.yarn.api.records.ResourceRequest;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.BaseFederationPoliciesTest;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.exceptions.FederationPolicyException;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterIdInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterState;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.utils.FederationPoliciesTestUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple test class for the {@link BroadcastAMRMProxyPolicy}.
|
||||||
|
*/
|
||||||
|
public class TestBroadcastAMRMProxyFederationPolicy
|
||||||
|
extends BaseFederationPoliciesTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
setPolicy(new BroadcastAMRMProxyPolicy());
|
||||||
|
// needed for base test to work
|
||||||
|
setPolicyInfo(mock(WeightedPolicyInfo.class));
|
||||||
|
|
||||||
|
for (int i = 1; i <= 2; i++) {
|
||||||
|
SubClusterIdInfo sc = new SubClusterIdInfo("sc" + i);
|
||||||
|
SubClusterInfo sci = mock(SubClusterInfo.class);
|
||||||
|
when(sci.getState()).thenReturn(SubClusterState.SC_RUNNING);
|
||||||
|
when(sci.getSubClusterId()).thenReturn(sc.toId());
|
||||||
|
getActiveSubclusters().put(sc.toId(), sci);
|
||||||
|
}
|
||||||
|
|
||||||
|
FederationPoliciesTestUtil.initializePolicyContext(getPolicy(),
|
||||||
|
mock(WeightedPolicyInfo.class), getActiveSubclusters());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSplitAllocateRequest() throws Exception {
|
||||||
|
// verify the request is broadcasted to all subclusters
|
||||||
|
String[] hosts = new String[] {"host1", "host2" };
|
||||||
|
List<ResourceRequest> resourceRequests = FederationPoliciesTestUtil
|
||||||
|
.createResourceRequests(hosts, 2 * 1024, 2, 1, 3, null, false);
|
||||||
|
|
||||||
|
Map<SubClusterId, List<ResourceRequest>> response =
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.splitResourceRequests(resourceRequests);
|
||||||
|
Assert.assertTrue(response.size() == 2);
|
||||||
|
for (Map.Entry<SubClusterId, List<ResourceRequest>> entry : response
|
||||||
|
.entrySet()) {
|
||||||
|
Assert.assertTrue(getActiveSubclusters().get(entry.getKey()) != null);
|
||||||
|
for (ResourceRequest r : entry.getValue()) {
|
||||||
|
Assert.assertTrue(resourceRequests.contains(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (SubClusterId subClusterId : getActiveSubclusters().keySet()) {
|
||||||
|
for (ResourceRequest r : response.get(subClusterId)) {
|
||||||
|
Assert.assertTrue(resourceRequests.contains(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotifyOfResponse() throws Exception {
|
||||||
|
String[] hosts = new String[] {"host1", "host2" };
|
||||||
|
List<ResourceRequest> resourceRequests = FederationPoliciesTestUtil
|
||||||
|
.createResourceRequests(hosts, 2 * 1024, 2, 1, 3, null, false);
|
||||||
|
Map<SubClusterId, List<ResourceRequest>> response =
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.splitResourceRequests(resourceRequests);
|
||||||
|
|
||||||
|
try {
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy()).notifyOfResponse(
|
||||||
|
SubClusterId.newInstance("sc3"), mock(AllocateResponse.class));
|
||||||
|
Assert.fail();
|
||||||
|
} catch (FederationPolicyException f) {
|
||||||
|
System.out.println("Expected: " + f.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy()).notifyOfResponse(
|
||||||
|
SubClusterId.newInstance("sc1"), mock(AllocateResponse.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,566 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.yarn.server.federation.policies.amrmproxy;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
|
||||||
|
import org.apache.hadoop.yarn.api.records.NMToken;
|
||||||
|
import org.apache.hadoop.yarn.api.records.NodeReport;
|
||||||
|
import org.apache.hadoop.yarn.api.records.Resource;
|
||||||
|
import org.apache.hadoop.yarn.api.records.ResourceRequest;
|
||||||
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.BaseFederationPoliciesTest;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.FederationPolicyInitializationContext;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.resolver.DefaultSubClusterResolverImpl;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.resolver.SubClusterResolver;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterIdInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterPolicyConfiguration;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterState;
|
||||||
|
import org.apache.hadoop.yarn.server.federation.utils.FederationPoliciesTestUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple test class for the {@link LocalityMulticastAMRMProxyPolicy}.
|
||||||
|
*/
|
||||||
|
public class TestLocalityMulticastAMRMProxyPolicy
|
||||||
|
extends BaseFederationPoliciesTest {
|
||||||
|
|
||||||
|
public static final Logger LOG =
|
||||||
|
LoggerFactory.getLogger(TestLocalityMulticastAMRMProxyPolicy.class);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
setPolicy(new LocalityMulticastAMRMProxyPolicy());
|
||||||
|
setPolicyInfo(new WeightedPolicyInfo());
|
||||||
|
Map<SubClusterIdInfo, Float> routerWeights = new HashMap<>();
|
||||||
|
Map<SubClusterIdInfo, Float> amrmWeights = new HashMap<>();
|
||||||
|
|
||||||
|
// simulate 20 subclusters with a 5% chance of being inactive
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
SubClusterIdInfo sc = new SubClusterIdInfo("subcluster" + i);
|
||||||
|
// sub-cluster 3 is not active
|
||||||
|
if (i != 3) {
|
||||||
|
SubClusterInfo sci = mock(SubClusterInfo.class);
|
||||||
|
when(sci.getState()).thenReturn(SubClusterState.SC_RUNNING);
|
||||||
|
when(sci.getSubClusterId()).thenReturn(sc.toId());
|
||||||
|
getActiveSubclusters().put(sc.toId(), sci);
|
||||||
|
}
|
||||||
|
|
||||||
|
float weight = 1 / 10f;
|
||||||
|
routerWeights.put(sc, weight);
|
||||||
|
amrmWeights.put(sc, weight);
|
||||||
|
// sub-cluster 4 is "disabled" in the weights
|
||||||
|
if (i == 4) {
|
||||||
|
routerWeights.put(sc, 0f);
|
||||||
|
amrmWeights.put(sc, 0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPolicyInfo().setRouterPolicyWeights(routerWeights);
|
||||||
|
getPolicyInfo().setAMRMPolicyWeights(amrmWeights);
|
||||||
|
getPolicyInfo().setHeadroomAlpha(0.5f);
|
||||||
|
setHomeSubCluster(SubClusterId.newInstance("homesubcluster"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReinitilialize() throws YarnException {
|
||||||
|
initializePolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializePolicy() throws YarnException {
|
||||||
|
setFederationPolicyContext(new FederationPolicyInitializationContext());
|
||||||
|
SubClusterResolver resolver = FederationPoliciesTestUtil.initResolver();
|
||||||
|
getFederationPolicyContext().setFederationSubclusterResolver(resolver);
|
||||||
|
ByteBuffer buf = getPolicyInfo().toByteBuffer();
|
||||||
|
getFederationPolicyContext().setSubClusterPolicyConfiguration(
|
||||||
|
SubClusterPolicyConfiguration.newInstance("queue1",
|
||||||
|
getPolicy().getClass().getCanonicalName(), buf));
|
||||||
|
getFederationPolicyContext().setHomeSubcluster(getHomeSubCluster());
|
||||||
|
FederationPoliciesTestUtil.initializePolicyContext(
|
||||||
|
getFederationPolicyContext(), getPolicy(), getPolicyInfo(),
|
||||||
|
getActiveSubclusters());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSplitBasedOnHeadroom() throws Exception {
|
||||||
|
|
||||||
|
// Tests how the headroom info are used to split based on the capacity
|
||||||
|
// each RM claims to give us.
|
||||||
|
// Configure policy to be 100% headroom based
|
||||||
|
getPolicyInfo().setHeadroomAlpha(1.0f);
|
||||||
|
|
||||||
|
initializePolicy();
|
||||||
|
List<ResourceRequest> resourceRequests = createSimpleRequest();
|
||||||
|
|
||||||
|
prepPolicyWithHeadroom();
|
||||||
|
|
||||||
|
Map<SubClusterId, List<ResourceRequest>> response =
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.splitResourceRequests(resourceRequests);
|
||||||
|
|
||||||
|
// pretty print requests
|
||||||
|
LOG.info("Initial headroom");
|
||||||
|
prettyPrintRequests(response);
|
||||||
|
|
||||||
|
validateSplit(response, resourceRequests);
|
||||||
|
|
||||||
|
// based on headroom, we expect 75 containers to got to subcluster0,
|
||||||
|
// as it advertise lots of headroom (100), no containers for sublcuster1
|
||||||
|
// as it advertise zero headroom, 1 to subcluster 2 (as it advertise little
|
||||||
|
// headroom (1), and 25 to subcluster5 which has unknown headroom, and so
|
||||||
|
// it gets 1/4th of the load
|
||||||
|
checkExpectedAllocation(response, "subcluster0", 1, 75);
|
||||||
|
checkExpectedAllocation(response, "subcluster1", 1, -1);
|
||||||
|
checkExpectedAllocation(response, "subcluster2", 1, 1);
|
||||||
|
checkExpectedAllocation(response, "subcluster5", 1, 25);
|
||||||
|
|
||||||
|
// notify a change in headroom and try again
|
||||||
|
AllocateResponse ar = getAllocateResponseWithTargetHeadroom(100);
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.notifyOfResponse(SubClusterId.newInstance("subcluster2"), ar);
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.splitResourceRequests(resourceRequests);
|
||||||
|
|
||||||
|
LOG.info("After headroom update");
|
||||||
|
prettyPrintRequests(response);
|
||||||
|
validateSplit(response, resourceRequests);
|
||||||
|
|
||||||
|
// we simulated a change in headroom for subcluster2, which will now
|
||||||
|
// have the same headroom of subcluster0 and so it splits the requests
|
||||||
|
// note that the total is still less or equal to (userAsk + numSubClusters)
|
||||||
|
checkExpectedAllocation(response, "subcluster0", 1, 38);
|
||||||
|
checkExpectedAllocation(response, "subcluster1", 1, -1);
|
||||||
|
checkExpectedAllocation(response, "subcluster2", 1, 38);
|
||||||
|
checkExpectedAllocation(response, "subcluster5", 1, 25);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 5000)
|
||||||
|
public void testStressPolicy() throws Exception {
|
||||||
|
|
||||||
|
// Tests how the headroom info are used to split based on the capacity
|
||||||
|
// each RM claims to give us.
|
||||||
|
// Configure policy to be 100% headroom based
|
||||||
|
getPolicyInfo().setHeadroomAlpha(1.0f);
|
||||||
|
|
||||||
|
initializePolicy();
|
||||||
|
|
||||||
|
int numRR = 1000;
|
||||||
|
List<ResourceRequest> resourceRequests = createLargeRandomList(numRR);
|
||||||
|
|
||||||
|
prepPolicyWithHeadroom();
|
||||||
|
|
||||||
|
int numIterations = 1000;
|
||||||
|
long tstart = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < numIterations; i++) {
|
||||||
|
Map<SubClusterId, List<ResourceRequest>> response =
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.splitResourceRequests(resourceRequests);
|
||||||
|
validateSplit(response, resourceRequests);
|
||||||
|
}
|
||||||
|
long tend = System.currentTimeMillis();
|
||||||
|
|
||||||
|
LOG.info("Performed " + numIterations + " policy invocations (and "
|
||||||
|
+ "validations) in " + (tend - tstart) + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFWDAllZeroANY() throws Exception {
|
||||||
|
|
||||||
|
// Tests how the headroom info are used to split based on the capacity
|
||||||
|
// each RM claims to give us.
|
||||||
|
// Configure policy to be 100% headroom based
|
||||||
|
getPolicyInfo().setHeadroomAlpha(0.5f);
|
||||||
|
|
||||||
|
initializePolicy();
|
||||||
|
List<ResourceRequest> resourceRequests = createZeroSizedANYRequest();
|
||||||
|
|
||||||
|
// this receives responses from sc0,sc1,sc2
|
||||||
|
prepPolicyWithHeadroom();
|
||||||
|
|
||||||
|
Map<SubClusterId, List<ResourceRequest>> response =
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.splitResourceRequests(resourceRequests);
|
||||||
|
|
||||||
|
// we expect all three to appear for a zero-sized ANY
|
||||||
|
|
||||||
|
// pretty print requests
|
||||||
|
prettyPrintRequests(response);
|
||||||
|
|
||||||
|
validateSplit(response, resourceRequests);
|
||||||
|
|
||||||
|
// we expect the zero size request to be sent to the first 3 rm (due to
|
||||||
|
// the fact that we received responses only from these 3 sublcusters)
|
||||||
|
checkExpectedAllocation(response, "subcluster0", 1, 0);
|
||||||
|
checkExpectedAllocation(response, "subcluster1", 1, 0);
|
||||||
|
checkExpectedAllocation(response, "subcluster2", 1, 0);
|
||||||
|
checkExpectedAllocation(response, "subcluster3", -1, -1);
|
||||||
|
checkExpectedAllocation(response, "subcluster4", -1, -1);
|
||||||
|
checkExpectedAllocation(response, "subcluster5", -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSplitBasedOnHeadroomAndWeights() throws Exception {
|
||||||
|
|
||||||
|
// Tests how the headroom info are used to split based on the capacity
|
||||||
|
// each RM claims to give us.
|
||||||
|
|
||||||
|
// Configure policy to be 50% headroom based and 50% weight based
|
||||||
|
getPolicyInfo().setHeadroomAlpha(0.5f);
|
||||||
|
|
||||||
|
initializePolicy();
|
||||||
|
List<ResourceRequest> resourceRequests = createSimpleRequest();
|
||||||
|
|
||||||
|
prepPolicyWithHeadroom();
|
||||||
|
|
||||||
|
Map<SubClusterId, List<ResourceRequest>> response =
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.splitResourceRequests(resourceRequests);
|
||||||
|
|
||||||
|
// pretty print requests
|
||||||
|
prettyPrintRequests(response);
|
||||||
|
|
||||||
|
validateSplit(response, resourceRequests);
|
||||||
|
|
||||||
|
// in this case the headroom allocates 50 containers, while weights allocate
|
||||||
|
// the rest. due to weights we have 12.5 (round to 13) containers for each
|
||||||
|
// sublcuster, the rest is due to headroom.
|
||||||
|
checkExpectedAllocation(response, "subcluster0", 1, 50);
|
||||||
|
checkExpectedAllocation(response, "subcluster1", 1, 13);
|
||||||
|
checkExpectedAllocation(response, "subcluster2", 1, 13);
|
||||||
|
checkExpectedAllocation(response, "subcluster3", -1, -1);
|
||||||
|
checkExpectedAllocation(response, "subcluster4", -1, -1);
|
||||||
|
checkExpectedAllocation(response, "subcluster5", 1, 25);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepPolicyWithHeadroom() throws YarnException {
|
||||||
|
AllocateResponse ar = getAllocateResponseWithTargetHeadroom(100);
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.notifyOfResponse(SubClusterId.newInstance("subcluster0"), ar);
|
||||||
|
|
||||||
|
ar = getAllocateResponseWithTargetHeadroom(0);
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.notifyOfResponse(SubClusterId.newInstance("subcluster1"), ar);
|
||||||
|
|
||||||
|
ar = getAllocateResponseWithTargetHeadroom(1);
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.notifyOfResponse(SubClusterId.newInstance("subcluster2"), ar);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AllocateResponse getAllocateResponseWithTargetHeadroom(
|
||||||
|
int numContainers) {
|
||||||
|
return AllocateResponse.newInstance(0, null, null,
|
||||||
|
Collections.<NodeReport> emptyList(),
|
||||||
|
Resource.newInstance(numContainers * 1024, numContainers), null, 10,
|
||||||
|
null, Collections.<NMToken> emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSplitAllocateRequest() throws Exception {
|
||||||
|
|
||||||
|
// Test a complex List<ResourceRequest> is split correctly
|
||||||
|
initializePolicy();
|
||||||
|
|
||||||
|
// modify default initialization to include a "homesubcluster"
|
||||||
|
// which we will use as the default for when nodes or racks are unknown
|
||||||
|
SubClusterInfo sci = mock(SubClusterInfo.class);
|
||||||
|
when(sci.getState()).thenReturn(SubClusterState.SC_RUNNING);
|
||||||
|
when(sci.getSubClusterId()).thenReturn(getHomeSubCluster());
|
||||||
|
getActiveSubclusters().put(getHomeSubCluster(), sci);
|
||||||
|
SubClusterIdInfo sc = new SubClusterIdInfo(getHomeSubCluster().getId());
|
||||||
|
|
||||||
|
getPolicyInfo().getRouterPolicyWeights().put(sc, 0.1f);
|
||||||
|
getPolicyInfo().getAMRMPolicyWeights().put(sc, 0.1f);
|
||||||
|
|
||||||
|
FederationPoliciesTestUtil.initializePolicyContext(
|
||||||
|
getFederationPolicyContext(), getPolicy(), getPolicyInfo(),
|
||||||
|
getActiveSubclusters());
|
||||||
|
|
||||||
|
List<ResourceRequest> resourceRequests = createComplexRequest();
|
||||||
|
|
||||||
|
Map<SubClusterId, List<ResourceRequest>> response =
|
||||||
|
((FederationAMRMProxyPolicy) getPolicy())
|
||||||
|
.splitResourceRequests(resourceRequests);
|
||||||
|
|
||||||
|
validateSplit(response, resourceRequests);
|
||||||
|
prettyPrintRequests(response);
|
||||||
|
|
||||||
|
// we expect 4 entry for home subcluster (3 for request-id 4, and a part
|
||||||
|
// of the broadcast of request-id 2
|
||||||
|
checkExpectedAllocation(response, getHomeSubCluster().getId(), 4, 23);
|
||||||
|
|
||||||
|
// for subcluster0 we expect 3 entry from request-id 0, and 3 from
|
||||||
|
// request-id 3, as well as part of the request-id 2 broadast
|
||||||
|
checkExpectedAllocation(response, "subcluster0", 7, 26);
|
||||||
|
|
||||||
|
// we expect 5 entry for subcluster1 (4 from request-id 1, and part
|
||||||
|
// of the broadcast of request-id 2
|
||||||
|
checkExpectedAllocation(response, "subcluster1", 5, 25);
|
||||||
|
|
||||||
|
// sub-cluster 2 should contain 3 entry from request-id 1 and 1 from the
|
||||||
|
// broadcast of request-id 2, and no request-id 0
|
||||||
|
checkExpectedAllocation(response, "subcluster2", 4, 23);
|
||||||
|
|
||||||
|
// subcluster id 3, 4 should not appear (due to weights or active/inactive)
|
||||||
|
checkExpectedAllocation(response, "subcluster3", -1, -1);
|
||||||
|
checkExpectedAllocation(response, "subcluster4", -1, -1);
|
||||||
|
|
||||||
|
// subcluster5 should get only part of the request-id 2 broadcast
|
||||||
|
checkExpectedAllocation(response, "subcluster5", 1, 20);
|
||||||
|
|
||||||
|
// check that the allocations that show up are what expected
|
||||||
|
for (ResourceRequest rr : response.get(getHomeSubCluster())) {
|
||||||
|
Assert.assertTrue(rr.getAllocationRequestId() == 4L
|
||||||
|
|| rr.getAllocationRequestId() == 2L);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ResourceRequest rr : response.get(getHomeSubCluster())) {
|
||||||
|
Assert.assertTrue(rr.getAllocationRequestId() != 1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ResourceRequest> rrs =
|
||||||
|
response.get(SubClusterId.newInstance("subcluster0"));
|
||||||
|
for (ResourceRequest rr : rrs) {
|
||||||
|
Assert.assertTrue(rr.getAllocationRequestId() != 1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ResourceRequest rr : response
|
||||||
|
.get(SubClusterId.newInstance("subcluster2"))) {
|
||||||
|
Assert.assertTrue(rr.getAllocationRequestId() != 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ResourceRequest rr : response
|
||||||
|
.get(SubClusterId.newInstance("subcluster5"))) {
|
||||||
|
Assert.assertTrue(rr.getAllocationRequestId() >= 2);
|
||||||
|
Assert.assertTrue(rr.getRelaxLocality());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that the number of containers in the first ResourceRequest in
|
||||||
|
// response for this sub-cluster matches expectations. -1 indicate the
|
||||||
|
// response should be null
|
||||||
|
private void checkExpectedAllocation(
|
||||||
|
Map<SubClusterId, List<ResourceRequest>> response, String subCluster,
|
||||||
|
long totResourceRequests, long totContainers) {
|
||||||
|
if (totContainers == -1) {
|
||||||
|
Assert.assertNull(response.get(SubClusterId.newInstance(subCluster)));
|
||||||
|
} else {
|
||||||
|
SubClusterId sc = SubClusterId.newInstance(subCluster);
|
||||||
|
Assert.assertEquals(totResourceRequests, response.get(sc).size());
|
||||||
|
|
||||||
|
long actualContCount = 0;
|
||||||
|
for (ResourceRequest rr : response.get(sc)) {
|
||||||
|
actualContCount += rr.getNumContainers();
|
||||||
|
}
|
||||||
|
Assert.assertEquals(totContainers, actualContCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateSplit(Map<SubClusterId, List<ResourceRequest>> split,
|
||||||
|
List<ResourceRequest> original) throws YarnException {
|
||||||
|
|
||||||
|
SubClusterResolver resolver =
|
||||||
|
getFederationPolicyContext().getFederationSubclusterResolver();
|
||||||
|
|
||||||
|
// Apply general validation rules
|
||||||
|
int numUsedSubclusters = split.size();
|
||||||
|
|
||||||
|
Set<Long> originalIds = new HashSet<>();
|
||||||
|
Set<Long> splitIds = new HashSet<>();
|
||||||
|
|
||||||
|
int originalContainers = 0;
|
||||||
|
for (ResourceRequest rr : original) {
|
||||||
|
originalContainers += rr.getNumContainers();
|
||||||
|
originalIds.add(rr.getAllocationRequestId());
|
||||||
|
}
|
||||||
|
|
||||||
|
int splitContainers = 0;
|
||||||
|
for (Map.Entry<SubClusterId, List<ResourceRequest>> rrs : split
|
||||||
|
.entrySet()) {
|
||||||
|
for (ResourceRequest rr : rrs.getValue()) {
|
||||||
|
splitContainers += rr.getNumContainers();
|
||||||
|
splitIds.add(rr.getAllocationRequestId());
|
||||||
|
// check node-local asks are sent to right RM (only)
|
||||||
|
SubClusterId fid = null;
|
||||||
|
try {
|
||||||
|
fid = resolver.getSubClusterForNode(rr.getResourceName());
|
||||||
|
} catch (YarnException e) {
|
||||||
|
// ignore code will handle
|
||||||
|
}
|
||||||
|
if (!rrs.getKey().equals(getHomeSubCluster()) && fid != null
|
||||||
|
&& !fid.equals(rrs.getKey())) {
|
||||||
|
Assert.fail("A node-local (or resolvable rack-local) RR should not "
|
||||||
|
+ "be send to an RM other than what it resolves to.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check we are not inventing Allocation Ids
|
||||||
|
Assert.assertEquals(originalIds, splitIds);
|
||||||
|
|
||||||
|
// check we are not exceedingly replicating the container asks among
|
||||||
|
// RMs (a little is allowed due to rounding of fractional splits)
|
||||||
|
Assert.assertTrue(
|
||||||
|
" Containers requested (" + splitContainers + ") should "
|
||||||
|
+ "not exceed the original count of containers ("
|
||||||
|
+ originalContainers + ") by more than the number of subclusters ("
|
||||||
|
+ numUsedSubclusters + ")",
|
||||||
|
originalContainers + numUsedSubclusters >= splitContainers);
|
||||||
|
|
||||||
|
// Test target Ids
|
||||||
|
for (SubClusterId targetId : split.keySet()) {
|
||||||
|
Assert.assertTrue("Target subclusters should be in the active set",
|
||||||
|
getActiveSubclusters().containsKey(targetId));
|
||||||
|
Assert.assertTrue(
|
||||||
|
"Target subclusters (" + targetId + ") should have weight >0 in "
|
||||||
|
+ "the policy ",
|
||||||
|
getPolicyInfo().getRouterPolicyWeights()
|
||||||
|
.get(new SubClusterIdInfo(targetId)) > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prettyPrintRequests(
|
||||||
|
Map<SubClusterId, List<ResourceRequest>> response) {
|
||||||
|
for (Map.Entry<SubClusterId, List<ResourceRequest>> entry : response
|
||||||
|
.entrySet()) {
|
||||||
|
String str = "";
|
||||||
|
for (ResourceRequest rr : entry.getValue()) {
|
||||||
|
str += " [id:" + rr.getAllocationRequestId() + " loc:"
|
||||||
|
+ rr.getResourceName() + " numCont:" + rr.getNumContainers()
|
||||||
|
+ "], ";
|
||||||
|
}
|
||||||
|
LOG.info(entry.getKey() + " --> " + str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResourceRequest> createLargeRandomList(int numRR)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
List<ResourceRequest> out = new ArrayList<>();
|
||||||
|
Random rand = new Random(1);
|
||||||
|
DefaultSubClusterResolverImpl resolver =
|
||||||
|
(DefaultSubClusterResolverImpl) getFederationPolicyContext()
|
||||||
|
.getFederationSubclusterResolver();
|
||||||
|
|
||||||
|
List<String> nodes =
|
||||||
|
new ArrayList<>(resolver.getNodeToSubCluster().keySet());
|
||||||
|
|
||||||
|
for (int i = 0; i < numRR; i++) {
|
||||||
|
String nodeName = nodes.get(rand.nextInt(nodes.size()));
|
||||||
|
long allocationId = (long) rand.nextInt(20);
|
||||||
|
|
||||||
|
// create a single container request in sc0
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(allocationId,
|
||||||
|
nodeName, 1024, 1, 1, rand.nextInt(100), null, rand.nextBoolean()));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResourceRequest> createSimpleRequest() throws Exception {
|
||||||
|
|
||||||
|
List<ResourceRequest> out = new ArrayList<>();
|
||||||
|
|
||||||
|
// create a single container request in sc0
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(0L,
|
||||||
|
ResourceRequest.ANY, 1024, 1, 1, 100, null, true));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResourceRequest> createZeroSizedANYRequest() throws Exception {
|
||||||
|
|
||||||
|
List<ResourceRequest> out = new ArrayList<>();
|
||||||
|
|
||||||
|
// create a single container request in sc0
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(0L,
|
||||||
|
ResourceRequest.ANY, 1024, 1, 1, 0, null, true));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResourceRequest> createComplexRequest() throws Exception {
|
||||||
|
|
||||||
|
List<ResourceRequest> out = new ArrayList<>();
|
||||||
|
|
||||||
|
// create a single container request in sc0
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(0L,
|
||||||
|
"subcluster0-rack0-host0", 1024, 1, 1, 1, null, false));
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(0L,
|
||||||
|
"subcluster0-rack0", 1024, 1, 1, 1, null, false));
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(0L,
|
||||||
|
ResourceRequest.ANY, 1024, 1, 1, 1, null, false));
|
||||||
|
|
||||||
|
// create a single container request with 3 alternative hosts across sc1,sc2
|
||||||
|
// where we want 2 containers in sc1 and 1 in sc2
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(1L,
|
||||||
|
"subcluster1-rack1-host1", 1024, 1, 1, 1, null, false));
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(1L,
|
||||||
|
"subcluster1-rack1-host2", 1024, 1, 1, 1, null, false));
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(1L,
|
||||||
|
"subcluster2-rack3-host3", 1024, 1, 1, 1, null, false));
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(1L,
|
||||||
|
"subcluster1-rack1", 1024, 1, 1, 2, null, false));
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(1L,
|
||||||
|
"subcluster2-rack3", 1024, 1, 1, 1, null, false));
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(1L,
|
||||||
|
ResourceRequest.ANY, 1024, 1, 1, 2, null, false));
|
||||||
|
|
||||||
|
// create a non-local ANY request that can span anything
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(2L,
|
||||||
|
ResourceRequest.ANY, 1024, 1, 1, 100, null, true));
|
||||||
|
|
||||||
|
// create a single container request in sc0 with relaxed locality
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(3L,
|
||||||
|
"subcluster0-rack0-host0", 1024, 1, 1, 1, null, true));
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(3L,
|
||||||
|
"subcluster0-rack0", 1024, 1, 1, 1, null, true));
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(3L,
|
||||||
|
ResourceRequest.ANY, 1024, 1, 1, 1, null, true));
|
||||||
|
|
||||||
|
// create a request of an unknown node/rack and expect this to show up
|
||||||
|
// in homesubcluster
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(4L, "unknownNode",
|
||||||
|
1024, 1, 1, 1, null, false));
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(4L, "unknownRack",
|
||||||
|
1024, 1, 1, 1, null, false));
|
||||||
|
out.add(FederationPoliciesTestUtil.createResourceRequest(4L,
|
||||||
|
ResourceRequest.ANY, 1024, 1, 1, 1, null, false));
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,9 @@
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.router;
|
package org.apache.hadoop.yarn.server.federation.policies.router;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.BaseFederationPoliciesTest;
|
import org.apache.hadoop.yarn.server.federation.policies.BaseFederationPoliciesTest;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
||||||
|
@ -29,12 +32,9 @@
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple test class for the {@link LoadBasedRouterPolicy}. Test that the
|
* Simple test class for the {@link LoadBasedRouterPolicy}. Test that the load
|
||||||
* load is properly considered for allocation.
|
* is properly considered for allocation.
|
||||||
*/
|
*/
|
||||||
public class TestLoadBasedRouterPolicy extends BaseFederationPoliciesTest {
|
public class TestLoadBasedRouterPolicy extends BaseFederationPoliciesTest {
|
||||||
|
|
||||||
|
@ -47,12 +47,10 @@ public void setUp() throws Exception {
|
||||||
|
|
||||||
// simulate 20 active subclusters
|
// simulate 20 active subclusters
|
||||||
for (int i = 0; i < 20; i++) {
|
for (int i = 0; i < 20; i++) {
|
||||||
SubClusterIdInfo sc =
|
SubClusterIdInfo sc = new SubClusterIdInfo(String.format("sc%02d", i));
|
||||||
new SubClusterIdInfo(String.format("sc%02d", i));
|
|
||||||
SubClusterInfo federationSubClusterInfo =
|
SubClusterInfo federationSubClusterInfo =
|
||||||
SubClusterInfo.newInstance(sc.toId(), null, null, null, null, -1,
|
SubClusterInfo.newInstance(sc.toId(), null, null, null, null, -1,
|
||||||
SubClusterState.SC_RUNNING, -1,
|
SubClusterState.SC_RUNNING, -1, generateClusterMetricsInfo(i));
|
||||||
generateClusterMetricsInfo(i));
|
|
||||||
getActiveSubclusters().put(sc.toId(), federationSubClusterInfo);
|
getActiveSubclusters().put(sc.toId(), federationSubClusterInfo);
|
||||||
float weight = getRand().nextInt(2);
|
float weight = getRand().nextInt(2);
|
||||||
if (i == 5) {
|
if (i == 5) {
|
||||||
|
@ -76,7 +74,7 @@ public void setUp() throws Exception {
|
||||||
private String generateClusterMetricsInfo(int id) {
|
private String generateClusterMetricsInfo(int id) {
|
||||||
|
|
||||||
long mem = 1024 * getRand().nextInt(277 * 100 - 1);
|
long mem = 1024 * getRand().nextInt(277 * 100 - 1);
|
||||||
//plant a best cluster
|
// plant a best cluster
|
||||||
if (id == 5) {
|
if (id == 5) {
|
||||||
mem = 1024 * 277 * 100;
|
mem = 1024 * 277 * 100;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,12 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.router;
|
package org.apache.hadoop.yarn.server.federation.policies.router;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.BaseFederationPoliciesTest;
|
import org.apache.hadoop.yarn.server.federation.policies.BaseFederationPoliciesTest;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
||||||
|
@ -28,12 +34,6 @@
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple test class for the {@link PriorityRouterPolicy}. Tests that the
|
* Simple test class for the {@link PriorityRouterPolicy}. Tests that the
|
||||||
* weights are correctly used for ordering the choice of sub-clusters.
|
* weights are correctly used for ordering the choice of sub-clusters.
|
||||||
|
@ -72,8 +72,7 @@ public void setUp() throws Exception {
|
||||||
getPolicyInfo().setRouterPolicyWeights(routerWeights);
|
getPolicyInfo().setRouterPolicyWeights(routerWeights);
|
||||||
getPolicyInfo().setAMRMPolicyWeights(amrmWeights);
|
getPolicyInfo().setAMRMPolicyWeights(amrmWeights);
|
||||||
FederationPoliciesTestUtil.initializePolicyContext(getPolicy(),
|
FederationPoliciesTestUtil.initializePolicyContext(getPolicy(),
|
||||||
getPolicyInfo(),
|
getPolicyInfo(), getActiveSubclusters());
|
||||||
getActiveSubclusters());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,13 @@
|
||||||
|
|
||||||
package org.apache.hadoop.yarn.server.federation.policies.router;
|
package org.apache.hadoop.yarn.server.federation.policies.router;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.BaseFederationPoliciesTest;
|
import org.apache.hadoop.yarn.server.federation.policies.BaseFederationPoliciesTest;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
import org.apache.hadoop.yarn.server.federation.policies.dao.WeightedPolicyInfo;
|
||||||
|
@ -29,13 +36,6 @@
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple test class for the {@link WeightedRandomRouterPolicy}. Generate large
|
* Simple test class for the {@link WeightedRandomRouterPolicy}. Generate large
|
||||||
* number of randomized tests to check we are weighiting correctly even if
|
* number of randomized tests to check we are weighiting correctly even if
|
||||||
|
@ -71,8 +71,7 @@ public void setUp() throws Exception {
|
||||||
getPolicyInfo().setAMRMPolicyWeights(amrmWeights);
|
getPolicyInfo().setAMRMPolicyWeights(amrmWeights);
|
||||||
|
|
||||||
FederationPoliciesTestUtil.initializePolicyContext(getPolicy(),
|
FederationPoliciesTestUtil.initializePolicyContext(getPolicy(),
|
||||||
getPolicyInfo(),
|
getPolicyInfo(), getActiveSubclusters());
|
||||||
getActiveSubclusters());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,8 +87,8 @@ public void testClusterChosenWithRightProbability() throws YarnException {
|
||||||
float numberOfDraws = 1000000;
|
float numberOfDraws = 1000000;
|
||||||
|
|
||||||
for (float i = 0; i < numberOfDraws; i++) {
|
for (float i = 0; i < numberOfDraws; i++) {
|
||||||
SubClusterId chosenId = ((FederationRouterPolicy) getPolicy()).
|
SubClusterId chosenId = ((FederationRouterPolicy) getPolicy())
|
||||||
getHomeSubcluster(getApplicationSubmissionContext());
|
.getHomeSubcluster(getApplicationSubmissionContext());
|
||||||
counter.get(chosenId).incrementAndGet();
|
counter.get(chosenId).incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,13 +112,15 @@ public void testClusterChosenWithRightProbability() throws YarnException {
|
||||||
if (getActiveSubclusters().containsKey(counterEntry.getKey())) {
|
if (getActiveSubclusters().containsKey(counterEntry.getKey())) {
|
||||||
Assert.assertTrue(
|
Assert.assertTrue(
|
||||||
"Id " + counterEntry.getKey() + " Actual weight: " + actualWeight
|
"Id " + counterEntry.getKey() + " Actual weight: " + actualWeight
|
||||||
+ " expected weight: " + expectedWeight, expectedWeight == 0 ||
|
+ " expected weight: " + expectedWeight,
|
||||||
(actualWeight / expectedWeight) < 1.1
|
expectedWeight == 0 || (actualWeight / expectedWeight) < 1.1
|
||||||
&& (actualWeight / expectedWeight) > 0.9);
|
&& (actualWeight / expectedWeight) > 0.9);
|
||||||
} else {
|
} else {
|
||||||
Assert.assertTrue(
|
Assert
|
||||||
"Id " + counterEntry.getKey() + " Actual weight: " + actualWeight
|
.assertTrue(
|
||||||
+ " expected weight: " + expectedWeight, actualWeight == 0);
|
"Id " + counterEntry.getKey() + " Actual weight: "
|
||||||
|
+ actualWeight + " expected weight: " + expectedWeight,
|
||||||
|
actualWeight == 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.apache.hadoop.yarn.server.federation.utils;
|
package org.apache.hadoop.yarn.server.federation.utils;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.yarn.api.records.*;
|
||||||
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||||
import org.apache.hadoop.yarn.exceptions.YarnException;
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
||||||
import org.apache.hadoop.yarn.server.federation.policies.ConfigurableFederationPolicy;
|
import org.apache.hadoop.yarn.server.federation.policies.ConfigurableFederationPolicy;
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
import org.apache.hadoop.yarn.server.federation.resolver.SubClusterResolver;
|
import org.apache.hadoop.yarn.server.federation.resolver.SubClusterResolver;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.FederationStateStore;
|
import org.apache.hadoop.yarn.server.federation.store.FederationStateStore;
|
||||||
import org.apache.hadoop.yarn.server.federation.store.records.*;
|
import org.apache.hadoop.yarn.server.federation.store.records.*;
|
||||||
|
import org.apache.hadoop.yarn.util.Records;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -48,6 +50,68 @@ private FederationPoliciesTestUtil() {
|
||||||
// disabled.
|
// disabled.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String FEDR_NODE_PREFIX = "fedr-test-node-";
|
||||||
|
|
||||||
|
|
||||||
|
public static List<ResourceRequest> createResourceRequests(String[] hosts,
|
||||||
|
int memory, int vCores, int priority, int containers,
|
||||||
|
String labelExpression, boolean relaxLocality) throws YarnException {
|
||||||
|
List<ResourceRequest> reqs = new ArrayList<ResourceRequest>();
|
||||||
|
for (String host : hosts) {
|
||||||
|
ResourceRequest hostReq =
|
||||||
|
createResourceRequest(host, memory, vCores, priority, containers,
|
||||||
|
labelExpression, relaxLocality);
|
||||||
|
reqs.add(hostReq);
|
||||||
|
ResourceRequest rackReq =
|
||||||
|
createResourceRequest("/default-rack", memory, vCores, priority,
|
||||||
|
containers, labelExpression, relaxLocality);
|
||||||
|
reqs.add(rackReq);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceRequest offRackReq =
|
||||||
|
createResourceRequest(ResourceRequest.ANY, memory, vCores, priority,
|
||||||
|
containers, labelExpression, relaxLocality);
|
||||||
|
reqs.add(offRackReq);
|
||||||
|
return reqs;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static ResourceRequest createResourceRequest(String resource,
|
||||||
|
int memory, int vCores, int priority, int containers,
|
||||||
|
boolean relaxLocality) throws YarnException {
|
||||||
|
return createResourceRequest(resource, memory, vCores, priority, containers,
|
||||||
|
null, relaxLocality);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:parameternumber")
|
||||||
|
public static ResourceRequest createResourceRequest(long id, String resource,
|
||||||
|
int memory, int vCores, int priority, int containers,
|
||||||
|
String labelExpression, boolean relaxLocality) throws YarnException {
|
||||||
|
ResourceRequest out =
|
||||||
|
createResourceRequest(resource, memory, vCores, priority, containers,
|
||||||
|
labelExpression, relaxLocality);
|
||||||
|
out.setAllocationRequestId(id);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResourceRequest createResourceRequest(String resource,
|
||||||
|
int memory, int vCores, int priority, int containers,
|
||||||
|
String labelExpression, boolean relaxLocality) throws YarnException {
|
||||||
|
ResourceRequest req = Records.newRecord(ResourceRequest.class);
|
||||||
|
req.setResourceName(resource);
|
||||||
|
req.setNumContainers(containers);
|
||||||
|
Priority pri = Records.newRecord(Priority.class);
|
||||||
|
pri.setPriority(priority);
|
||||||
|
req.setPriority(pri);
|
||||||
|
Resource capability = Records.newRecord(Resource.class);
|
||||||
|
capability.setMemorySize(memory);
|
||||||
|
capability.setVirtualCores(vCores);
|
||||||
|
req.setCapability(capability);
|
||||||
|
if (labelExpression != null) {
|
||||||
|
req.setNodeLabelExpression(labelExpression);
|
||||||
|
}
|
||||||
|
req.setRelaxLocality(relaxLocality);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
public static void initializePolicyContext(
|
public static void initializePolicyContext(
|
||||||
FederationPolicyInitializationContext fpc, ConfigurableFederationPolicy
|
FederationPolicyInitializationContext fpc, ConfigurableFederationPolicy
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
node1,subcluster1,rack1
|
node1,subcluster1,rack1
|
||||||
node2 , subcluster2, RACK1
|
node2 , subcluster2, RACK1
|
||||||
noDE3,subcluster3, rack2
|
noDE3,subcluster3, rack2
|
||||||
node4, subcluster3, rack2
|
node4, subcluster3, rack2
|
||||||
|
subcluster0-rack0-host0,subcluster0, subcluster0-rack0
|
||||||
|
Subcluster1-RACK1-HOST1,subcluster1, subCluster1-RACK1
|
||||||
|
SUBCLUSTER1-RACK1-HOST2,subcluster1, subCluster1-RACK1
|
||||||
|
SubCluster2-RACK3-HOST3,subcluster2, subcluster2-rack3
|
||||||
|
|
Loading…
Reference in New Issue