This is an automated email from the ASF dual-hosted git repository. hulee pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/helix.git
commit b43681634820ea5990851efae941e1123c5b4c54 Author: Yi Wang <[email protected]> AuthorDate: Tue Mar 12 16:59:25 2019 -0700 Util methods for checking if instance healthy RB=1585486 G=helix-reviewers A=jxue Signed-off-by: Hunter Lee <[email protected]> --- .gitignore | 3 +- .../main/java/org/apache/helix/PropertyKey.java | 2 +- .../apache/helix/util/InstanceValidationUtil.java | 87 ++++++++++++++++++++-- .../helix/rest/common/ZKReadAccessorWrapper.java | 50 +++++++++++++ 4 files changed, 133 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 27c2cdf..5cd0404 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,6 @@ target/ .settings/ out/ .DS_Store -#this directory will be part of release process +#this directory will be part of release process helix-dev-release/ +.shelf/ diff --git a/helix-core/src/main/java/org/apache/helix/PropertyKey.java b/helix-core/src/main/java/org/apache/helix/PropertyKey.java index 2ddc8bb..03d369b 100644 --- a/helix-core/src/main/java/org/apache/helix/PropertyKey.java +++ b/helix-core/src/main/java/org/apache/helix/PropertyKey.java @@ -22,8 +22,8 @@ package org.apache.helix; import static org.apache.helix.PropertyType.*; import java.util.Arrays; - import java.util.Objects; + import org.apache.helix.model.ClusterConfig; import org.apache.helix.model.ClusterConstraints; import org.apache.helix.model.ControllerHistory; diff --git a/helix-core/src/main/java/org/apache/helix/util/InstanceValidationUtil.java b/helix-core/src/main/java/org/apache/helix/util/InstanceValidationUtil.java index 23aa89b..b701864 100644 --- a/helix-core/src/main/java/org/apache/helix/util/InstanceValidationUtil.java +++ b/helix-core/src/main/java/org/apache/helix/util/InstanceValidationUtil.java @@ -25,21 +25,25 @@ import java.util.Map; import org.apache.helix.AccessOption; import org.apache.helix.ConfigAccessor; import org.apache.helix.HelixDataAccessor; +import org.apache.helix.HelixDefinedState; +import org.apache.helix.HelixException; import org.apache.helix.PropertyKey; import org.apache.helix.model.ClusterConfig; import org.apache.helix.model.CurrentState; import org.apache.helix.model.InstanceConfig; import org.apache.helix.model.LiveInstance; - +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Utility class for validating Helix properties * Warning: each method validates one single property of instance individually and independently. * One validation wouldn't depend on the results of other validations - * TODO: integrate on-demand cache if the performance is the bottleneck - * TODO: manually tested the function, need to add detailed integration test + * TODO: add unit tests */ public class InstanceValidationUtil { + private static final Logger _logger = LoggerFactory.getLogger(InstanceValidationUtil.class); + private InstanceValidationUtil() { } @@ -53,6 +57,7 @@ public class InstanceValidationUtil { */ public static boolean isEnabled(HelixDataAccessor dataAccessor, ConfigAccessor configAccessor, String clusterId, String instanceName) { + // TODO use static builder instance to reduce GC PropertyKey propertyKey = new PropertyKey.Builder(clusterId).instanceConfig(instanceName); InstanceConfig instanceConfig = dataAccessor.getProperty(propertyKey); ClusterConfig clusterConfig = configAccessor.getClusterConfig(clusterId); @@ -95,24 +100,92 @@ public class InstanceValidationUtil { */ public static boolean hasResourceAssigned(HelixDataAccessor dataAccessor, String clusterId, String instanceName) { - PropertyKey liveInstanceKey = new PropertyKey.Builder(clusterId).liveInstance(instanceName); + PropertyKey.Builder propertyKeyBuilder = new PropertyKey.Builder(clusterId); + PropertyKey liveInstanceKey = propertyKeyBuilder.liveInstance(instanceName); LiveInstance liveInstance = dataAccessor.getProperty(liveInstanceKey); if (liveInstance != null) { String sessionId = liveInstance.getSessionId(); PropertyKey currentStatesKey = - new PropertyKey.Builder(clusterId).currentStates(instanceName, sessionId); + propertyKeyBuilder.currentStates(instanceName, sessionId); List<String> resourceNames = dataAccessor.getChildNames(currentStatesKey); for (String resourceName : resourceNames) { PropertyKey key = - dataAccessor.keyBuilder().currentState(instanceName, sessionId, resourceName); + propertyKeyBuilder.currentState(instanceName, sessionId, resourceName); + CurrentState currentState = dataAccessor.getProperty(key); + if (currentState != null && currentState.getPartitionStateMap().size() > 0) { + return true; + } + } + } + + _logger.warn(String.format("The instance %s is not active", instanceName)); + return false; + } + + /** + * Method to check if the instance has any disabled partition assigned + * @param dataAccessor + * @param clusterId + * @param instanceName + * @return + */ + public static boolean hasDisabledPartitions(HelixDataAccessor dataAccessor, String clusterId, + String instanceName) { + PropertyKey propertyKey = new PropertyKey.Builder(clusterId).instanceConfig(instanceName); + InstanceConfig instanceConfig = dataAccessor.getProperty(propertyKey); + if (instanceConfig != null) { + return !instanceConfig.getDisabledPartitionsMap().isEmpty(); + } + + throw new HelixException("Fail to get instance config for " + instanceName); + } + + /** + * Method to check if the instance has valid configuration + * @param dataAccessor + * @param clusterId + * @param instanceName + * @return + */ + public static boolean hasValidConfig(HelixDataAccessor dataAccessor, String clusterId, + String instanceName) { + PropertyKey propertyKey = new PropertyKey.Builder(clusterId).instanceConfig(instanceName); + InstanceConfig instanceConfig = dataAccessor.getProperty(propertyKey); + return instanceConfig != null && instanceConfig.isValid(); + } + + /** + * Method to check if the instance has error partitions + * @param dataAccessor + * @param clusterId + * @param instanceName + * @return + */ + public static boolean hasErrorPartitions(HelixDataAccessor dataAccessor, String clusterId, + String instanceName) { + PropertyKey.Builder propertyKeyBuilder = new PropertyKey.Builder(clusterId); + PropertyKey liveInstanceKey = propertyKeyBuilder.liveInstance(instanceName); + LiveInstance liveInstance = dataAccessor.getProperty(liveInstanceKey); + if (liveInstance != null) { + String sessionId = liveInstance.getSessionId(); + + PropertyKey currentStatesKey = + propertyKeyBuilder.currentStates(instanceName, sessionId); + List<String> resourceNames = dataAccessor.getChildNames(currentStatesKey); + for (String resourceName : resourceNames) { + PropertyKey key = + propertyKeyBuilder.currentState(instanceName, sessionId, resourceName); + CurrentState currentState = dataAccessor.getProperty(key); - if (currentState.getPartitionStateMap().size() > 0) { + if (currentState != null + && currentState.getPartitionStateMap().containsValue(HelixDefinedState.ERROR.name())) { return true; } } } + _logger.warn(String.format("The instance %s is not active", instanceName)); return false; } } diff --git a/helix-rest/src/main/java/org/apache/helix/rest/common/ZKReadAccessorWrapper.java b/helix-rest/src/main/java/org/apache/helix/rest/common/ZKReadAccessorWrapper.java new file mode 100644 index 0000000..f8a4f4f --- /dev/null +++ b/helix-rest/src/main/java/org/apache/helix/rest/common/ZKReadAccessorWrapper.java @@ -0,0 +1,50 @@ +package org.apache.helix.rest.common; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.helix.BaseDataAccessor; +import org.apache.helix.HelixProperty; +import org.apache.helix.InstanceType; +import org.apache.helix.PropertyKey; +import org.apache.helix.ZNRecord; +import org.apache.helix.manager.zk.ZKHelixDataAccessor; + + +/** + * A read-only wrapper of {@link ZKHelixDataAccessor} with transient cache + * The caches is of the value from get methods and short lived for the lifecycle of one rest request + * TODO: add more cached read method based on needs + */ +public class ZKReadAccessorWrapper extends ZKHelixDataAccessor { + private final Map<PropertyKey, HelixProperty> _propertyCache = new HashMap<>(); + private final Map<PropertyKey, List<String>> _batchNameCache = new HashMap<>(); + + public ZKReadAccessorWrapper(String clusterName, InstanceType instanceType, + BaseDataAccessor<ZNRecord> baseDataAccessor) { + super(clusterName, instanceType, baseDataAccessor); + } + + @Override + public <T extends HelixProperty> T getProperty(PropertyKey key) { + if (_propertyCache.containsKey(key)) { + return (T) _propertyCache.get(key); + } + T property = super.getProperty(key); + _propertyCache.put(key, property); + return property; + } + + @Override + public List<String> getChildNames(PropertyKey key) { + if (_batchNameCache.containsKey(key)) { + return _batchNameCache.get(key); + } + + List<String> names = super.getChildNames(key); + _batchNameCache.put(key, names); + + return names; + } +}
