This is an automated email from the ASF dual-hosted git repository.
jiajunwang pushed a commit to branch revert-724-wagedRebalancer
in repository https://gitbox.apache.org/repos/asf/helix.git
commit 03798314bfdad43bde5764f39579dd6ecfff7ace
Author: Jiajun Wang <1803880+jiajunwang@users.noreply.github.com>
AuthorDate: Fri Feb 7 12:20:40 2020 -0800
Revert "Merge Waged rebalancer branch code to master. (#724)"
This reverts commit bee3ed273424b9614082be8c76eb80428a97ff3a.
---
helix-core/helix-core-0.9.2-SNAPSHOT.ivy | 3 +-
helix-core/pom.xml | 7 +-
.../java/org/apache/helix/BucketDataAccessor.java | 53 --
.../src/main/java/org/apache/helix/HelixAdmin.java | 47 --
.../org/apache/helix/HelixRebalanceException.java | 51 --
.../main/java/org/apache/helix/InstanceType.java | 6 +-
.../java/org/apache/helix/SystemPropertyKeys.java | 2 -
.../helix/controller/GenericHelixController.java | 129 +---
.../controller/changedetector/ChangeDetector.java | 57 --
.../changedetector/ResourceChangeDetector.java | 199 ------
.../changedetector/ResourceChangeSnapshot.java | 157 ----
.../ResourceControllerDataProvider.java | 34 +-
.../rebalancer/DelayedAutoRebalancer.java | 203 +++++-
.../controller/rebalancer/StatefulRebalancer.java | 37 -
.../rebalancer/util/DelayedRebalanceUtil.java | 267 -------
.../rebalancer/util/ResourceUsageCalculator.java | 192 -----
.../rebalancer/util/WagedValidationUtil.java | 91 ---
.../rebalancer/waged/AssignmentMetadataStore.java | 213 ------
.../rebalancer/waged/RebalanceAlgorithm.java | 43 --
.../rebalancer/waged/WagedRebalancer.java | 787 ---------------------
.../constraints/ConstraintBasedAlgorithm.java | 228 ------
.../ConstraintBasedAlgorithmFactory.java | 82 ---
.../constraints/FaultZoneAwareConstraint.java | 43 --
.../waged/constraints/HardConstraint.java | 47 --
.../InstancePartitionsCountConstraint.java | 41 --
.../MaxCapacityUsageInstanceConstraint.java | 42 --
.../waged/constraints/NodeCapacityConstraint.java | 50 --
.../NodeMaxPartitionLimitConstraint.java | 43 --
.../constraints/PartitionMovementConstraint.java | 96 ---
.../constraints/ReplicaActivateConstraint.java | 41 --
.../ResourcePartitionAntiAffinityConstraint.java | 43 --
.../ResourceTopStateAntiAffinityConstraint.java | 44 --
.../SamePartitionOnInstanceConstraint.java | 39 -
.../waged/constraints/SoftConstraint.java | 90 ---
.../waged/constraints/UsageSoftConstraint.java | 85 ---
.../waged/constraints/ValidGroupTagConstraint.java | 41 --
.../rebalancer/waged/model/AssignableNode.java | 374 ----------
.../rebalancer/waged/model/AssignableReplica.java | 161 -----
.../rebalancer/waged/model/ClusterContext.java | 172 -----
.../rebalancer/waged/model/ClusterModel.java | 132 ----
.../waged/model/ClusterModelProvider.java | 532 --------------
.../rebalancer/waged/model/OptimalAssignment.java | 93 ---
.../helix/controller/stages/AttributeName.java | 3 +-
.../stages/BestPossibleStateCalcStage.java | 180 +----
.../stages/CurrentStateComputationStage.java | 75 --
.../controller/stages/CurrentStateOutput.java | 23 -
.../org/apache/helix/manager/zk/ZKHelixAdmin.java | 282 +-------
.../manager/zk/ZNRecordJacksonSerializer.java | 67 --
.../helix/manager/zk/ZkBucketDataAccessor.java | 380 ----------
.../java/org/apache/helix/model/ClusterConfig.java | 228 +-----
.../org/apache/helix/model/InstanceConfig.java | 52 +-
.../org/apache/helix/model/ResourceConfig.java | 129 +---
.../apache/helix/model/StateModelDefinition.java | 4 +-
.../monitoring/mbeans/ClusterStatusMonitor.java | 92 +--
.../helix/monitoring/mbeans/InstanceMonitor.java | 192 +----
.../InstanceMonitorMBean.java} | 31 +-
.../monitoring/mbeans/MonitorDomainNames.java | 3 +-
.../helix/monitoring/mbeans/ResourceMonitor.java | 99 +--
.../mbeans/dynamicMBeans/DynamicMBeanProvider.java | 78 +-
.../mbeans/dynamicMBeans/SimpleDynamicMetric.java | 2 +-
.../helix/monitoring/metrics/MetricCollector.java | 99 ---
.../metrics/WagedRebalancerMetricCollector.java | 125 ----
.../implementation/BaselineDivergenceGauge.java | 68 --
.../metrics/implementation/RebalanceCounter.java | 36 -
.../implementation/RebalanceFailureCount.java | 34 -
.../implementation/RebalanceLatencyGauge.java | 89 ---
.../monitoring/metrics/model/CountMetric.java | 69 --
.../monitoring/metrics/model/LatencyMetric.java | 67 --
.../monitoring/metrics/model/RatioMetric.java | 58 --
.../BestPossibleExternalViewVerifier.java | 73 +-
.../StrictMatchExternalViewVerifier.java | 55 +-
.../ClusterVerifiers/ZkHelixClusterVerifier.java | 17 +
.../main/java/org/apache/helix/util/HelixUtil.java | 25 +-
.../java/org/apache/helix/util/RebalanceUtil.java | 7 +-
.../resources/soft-constraint-weight.properties | 26 -
.../java/org/apache/helix/common/ZkTestBase.java | 16 +-
.../changedetector/TestResourceChangeDetector.java | 441 ------------
.../util/TestResourceUsageCalculator.java | 103 ---
.../waged/MockAssignmentMetadataStore.java | 60 --
.../waged/TestAssignmentMetadataStore.java | 186 -----
.../rebalancer/waged/TestWagedRebalancer.java | 524 --------------
.../waged/TestWagedRebalancerMetrics.java | 190 -----
.../waged/constraints/MockRebalanceAlgorithm.java | 84 ---
.../constraints/TestConstraintBasedAlgorithm.java | 72 --
.../constraints/TestFaultZoneAwareConstraint.java | 79 ---
.../TestInstancePartitionsCountConstraint.java | 63 --
.../TestMaxCapacityUsageInstanceConstraint.java | 57 --
.../constraints/TestNodeCapacityConstraint.java | 54 --
.../TestNodeMaxPartitionLimitConstraint.java | 56 --
.../TestPartitionActivateConstraint.java | 64 --
.../TestPartitionMovementConstraint.java | 127 ----
...estResourcePartitionAntiAffinityConstraint.java | 67 --
...TestResourceTopStateAntiAffinityConstraint.java | 82 ---
.../TestSamePartitionOnInstanceConstraint.java | 59 --
.../TestSoftConstraintNormalizeFunction.java | 47 --
.../constraints/TestValidGroupTagConstraint.java | 66 --
.../waged/model/AbstractTestClusterModel.java | 204 ------
.../waged/model/ClusterModelTestHelper.java | 40 --
.../rebalancer/waged/model/TestAssignableNode.java | 280 --------
.../waged/model/TestAssignableReplica.java | 167 -----
.../rebalancer/waged/model/TestClusterContext.java | 93 ---
.../rebalancer/waged/model/TestClusterModel.java | 101 ---
.../waged/model/TestClusterModelProvider.java | 376 ----------
.../waged/model/TestOptimalAssignment.java | 91 ---
.../TestCrushAutoRebalanceNonRack.java | 8 +-
.../rebalancer/CrushRebalancers/TestNodeSwap.java | 4 +-
.../TestDelayedAutoRebalance.java | 49 +-
...stDelayedAutoRebalanceWithDisabledInstance.java | 32 +-
.../TestDelayedAutoRebalanceWithRackaware.java | 2 +-
.../PartitionMigration/TestExpandCluster.java | 4 +-
.../TestPartitionMigrationBase.java | 26 +-
.../TestWagedRebalancerMigration.java | 111 ---
.../rebalancer/TestMixedModeAutoRebalance.java | 181 ++---
.../rebalancer/TestZeroReplicaAvoidance.java | 62 +-
.../WagedRebalancer/TestDelayedWagedRebalance.java | 89 ---
...tDelayedWagedRebalanceWithDisabledInstance.java | 96 ---
.../TestDelayedWagedRebalanceWithRackaware.java | 96 ---
.../TestMixedModeWagedRebalance.java | 58 --
.../WagedRebalancer/TestWagedExpandCluster.java | 159 -----
.../WagedRebalancer/TestWagedNodeSwap.java | 294 --------
.../WagedRebalancer/TestWagedRebalance.java | 590 ---------------
.../TestWagedRebalanceFaultZone.java | 372 ----------
.../TestWagedRebalanceTopologyAware.java | 114 ---
.../helix/manager/zk/TestZkBucketDataAccessor.java | 189 -----
.../apache/helix/manager/zk/TestZkHelixAdmin.java | 108 ---
.../java/org/apache/helix/mock/MockHelixAdmin.java | 22 -
.../org/apache/helix/model/TestClusterConfig.java | 259 -------
.../org/apache/helix/model/TestInstanceConfig.java | 71 +-
.../org/apache/helix/model/TestResourceConfig.java | 186 -----
.../mbeans/TestClusterStatusMonitor.java | 192 +----
.../monitoring/mbeans/TestInstanceMonitor.java | 75 --
.../monitoring/mbeans/TestResourceMonitor.java | 371 ++++------
.../mbeans/TestRoutingTableProviderMonitor.java | 10 +-
.../monitoring/mbeans/TestZkClientMonitor.java | 9 +-
.../apache/helix/tools/TestClusterVerifier.java | 76 +-
...eUsageCalculator.MeasureBaselineDivergence.json | 37 -
...chExternalViewVerifier.ComputeIdealMapping.json | 14 +-
.../rest/server/resources/AbstractResource.java | 10 +-
.../server/resources/helix/ClusterAccessor.java | 10 +-
.../server/resources/helix/InstancesAccessor.java | 88 +--
.../resources/helix/PerInstanceAccessor.java | 49 +-
.../server/resources/helix/ResourceAccessor.java | 152 +---
.../helix/rest/server/AbstractTestClass.java | 5 +-
.../helix/rest/server/TestClusterAccessor.java | 13 -
.../helix/rest/server/TestInstancesAccessor.java | 77 --
.../helix/rest/server/TestPerInstanceAccessor.java | 66 --
.../helix/rest/server/TestResourceAccessor.java | 117 +--
.../rest/server/util/JerseyUriRequestBuilder.java | 3 +-
148 files changed, 895 insertions(+), 15375 deletions(-)
diff --git a/helix-core/helix-core-0.9.2-SNAPSHOT.ivy b/helix-core/helix-core-0.9.2-SNAPSHOT.ivy
index 07dd266..2d6e298 100644
--- a/helix-core/helix-core-0.9.2-SNAPSHOT.ivy
+++ b/helix-core/helix-core-0.9.2-SNAPSHOT.ivy
@@ -57,8 +57,7 @@ under the License.
<dependency org="org.codehaus.jackson" name="jackson-mapper-asl" rev="1.8.5" conf="compile->compile(default);runtime->runtime(default);default->default"/>
<dependency org="commons-io" name="commons-io" rev="1.4" conf="compile->compile(default);runtime->runtime(default);default->default"/>
<dependency org="commons-cli" name="commons-cli" rev="1.2" conf="compile->compile(default);runtime->runtime(default);default->default"/>
- <dependency org="org.apache.commons" name="commons-math" rev="2.1" conf="compile->compile(default);runtime->runtime(default);default->default"/>
- <dependency org="org.apache.commons" name="commons-math3" rev="3.6.1" conf="compile->compile(default);runtime->runtime(default);default->default"/>
+ <dependency org="commons-math" name="commons-math" rev="2.1" conf="compile->compile(default);runtime->runtime(default);default->default"/>
<dependency org="com.101tec" name="zkclient" rev="0.5" conf="compile->compile(default);runtime->runtime(default);default->default"/>
<dependency org="com.google.guava" name="guava" rev="15.0" conf="compile->compile(default);runtime->runtime(default);default->default"/>
<dependency org="org.yaml" name="snakeyaml" rev="1.12" conf="compile->compile(default);runtime->runtime(default);default->default"/>
diff --git a/helix-core/pom.xml b/helix-core/pom.xml
index 1077cc0..45b6552 100644
--- a/helix-core/pom.xml
+++ b/helix-core/pom.xml
@@ -37,7 +37,7 @@ under the License.
org.I0Itec.zkclient*,
org.apache.commons.cli*;version="[1.2,2)",
org.apache.commons.io*;version="[1.4,2)",
- org.apache.commons.math*;version="[2.1,4)",
+ org.apache.commons.math*;version="[2.1,3)",
org.apache.jute*;resolution:=optional,
org.apache.zookeeper.server.persistence*;resolution:=optional,
org.apache.zookeeper.server.util*;resolution:=optional,
@@ -140,11 +140,6 @@ under the License.
<version>2.1</version>
</dependency>
<dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-math3</artifactId>
- <version>3.6.1</version>
- </dependency>
- <dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.6</version>
diff --git a/helix-core/src/main/java/org/apache/helix/BucketDataAccessor.java b/helix-core/src/main/java/org/apache/helix/BucketDataAccessor.java
deleted file mode 100644
index 2008c23..0000000
--- a/helix-core/src/main/java/org/apache/helix/BucketDataAccessor.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package org.apache.helix;
-/*
- * 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.
- */
-
-import java.io.IOException;
-
-public interface BucketDataAccessor {
-
- /**
- * Write a HelixProperty in buckets, compressed.
- * @param path path to which the metadata will be written to
- * @param value HelixProperty to write
- * @param <T>
- * @throws IOException
- */
- <T extends HelixProperty> boolean compressedBucketWrite(String path, T value) throws IOException;
-
- /**
- * Read a HelixProperty that was written in buckets, compressed.
- * @param path
- * @param helixPropertySubType the subtype of HelixProperty the data was written in
- * @param <T>
- */
- <T extends HelixProperty> HelixProperty compressedBucketRead(String path,
- Class<T> helixPropertySubType);
-
- /**
- * Delete the HelixProperty in the given path.
- * @param path
- */
- void compressedBucketDelete(String path);
-
- /**
- * Close the connection to the metadata store.
- */
- void disconnect();
-}
diff --git a/helix-core/src/main/java/org/apache/helix/HelixAdmin.java b/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
index 423f879..a11b235 100644
--- a/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
+++ b/helix-core/src/main/java/org/apache/helix/HelixAdmin.java
@@ -31,7 +31,6 @@ import org.apache.helix.model.HelixConfigScope;
import org.apache.helix.model.IdealState;
import org.apache.helix.model.InstanceConfig;
import org.apache.helix.model.MaintenanceSignal;
-import org.apache.helix.model.ResourceConfig;
import org.apache.helix.model.StateModelDefinition;
/*
@@ -580,50 +579,4 @@ public interface HelixAdmin {
default void close() {
System.out.println("Default close() was invoked! No operation was executed.");
}
-
- /**
- * Adds a resource with IdealState and ResourceConfig to be rebalanced by WAGED rebalancer with validation.
- * Validation includes the following:
- * 1. Check ResourceConfig has the WEIGHT field
- * 2. Check that all capacity keys from ClusterConfig are set up in the WEIGHT field
- * 3. Check that all ResourceConfig's weightMap fields have all of the capacity keys
- * @param clusterName
- * @param idealState
- * @param resourceConfig
- * @return true if the resource has been added successfully. False otherwise
- */
- boolean addResourceWithWeight(String clusterName, IdealState idealState,
- ResourceConfig resourceConfig);
-
- /**
- * Batch-enables Waged rebalance for the names of resources given.
- * @param clusterName
- * @param resourceNames
- * @return
- */
- boolean enableWagedRebalance(String clusterName, List<String> resourceNames);
-
- /**
- * Validates the resources to see if their weight configs have been set properly.
- * Validation includes the following:
- * 1. Check ResourceConfig has the WEIGHT field
- * 2. Check that all capacity keys from ClusterConfig are set up in the WEIGHT field
- * 3. Check that all ResourceConfig's weightMap fields have all of the capacity keys
- * @param resourceNames
- * @return for each resource, true if the weight configs have been set properly, false otherwise
- */
- Map<String, Boolean> validateResourcesForWagedRebalance(String clusterName,
- List<String> resourceNames);
-
- /**
- * Validates the instances to ensure their weights in InstanceConfigs have been set up properly.
- * Validation includes the following:
- * 1. If default instance capacity is not set, check that the InstanceConfigs have the CAPACITY field
- * 2. Check that all capacity keys defined in ClusterConfig are present in the CAPACITY field
- * @param clusterName
- * @param instancesNames
- * @return
- */
- Map<String, Boolean> validateInstancesForWagedRebalance(String clusterName,
- List<String> instancesNames);
}
diff --git a/helix-core/src/main/java/org/apache/helix/HelixRebalanceException.java b/helix-core/src/main/java/org/apache/helix/HelixRebalanceException.java
deleted file mode 100644
index d54853f..0000000
--- a/helix-core/src/main/java/org/apache/helix/HelixRebalanceException.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.apache.helix;
-
-/*
- * 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.
- */
-
-/**
- * Exception thrown by Helix due to rebalance failures.
- */
-public class HelixRebalanceException extends Exception {
- // TODO: Adding static description or other necessary fields into the enum instances for
- // TODO: supporting the rebalance monitor to understand the exception.
- public enum Type {
- INVALID_CLUSTER_STATUS,
- INVALID_REBALANCER_STATUS,
- FAILED_TO_CALCULATE,
- INVALID_INPUT,
- UNKNOWN_FAILURE
- }
-
- private final Type _type;
-
- public HelixRebalanceException(String message, Type type, Throwable cause) {
- super(String.format("%s Failure Type: %s", message, type.name()), cause);
- _type = type;
- }
-
- public HelixRebalanceException(String message, Type type) {
- super(String.format("%s Failure Type: %s", message, type.name()));
- _type = type;
- }
-
- public Type getFailureType() {
- return _type;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/InstanceType.java b/helix-core/src/main/java/org/apache/helix/InstanceType.java
index 92b0e80..84e9d87 100644
--- a/helix-core/src/main/java/org/apache/helix/InstanceType.java
+++ b/helix-core/src/main/java/org/apache/helix/InstanceType.java
@@ -36,8 +36,7 @@ public enum InstanceType {
CONTROLLER(new String[] {
MonitorDomainNames.ClusterStatus.name(),
MonitorDomainNames.HelixZkClient.name(),
- MonitorDomainNames.HelixCallback.name(),
- MonitorDomainNames.Rebalancer.name()
+ MonitorDomainNames.HelixCallback.name()
}),
PARTICIPANT(new String[] {
@@ -52,8 +51,7 @@ public enum InstanceType {
MonitorDomainNames.HelixZkClient.name(),
MonitorDomainNames.HelixCallback.name(),
MonitorDomainNames.HelixThreadPoolExecutor.name(),
- MonitorDomainNames.CLMParticipantReport.name(),
- MonitorDomainNames.Rebalancer.name()
+ MonitorDomainNames.CLMParticipantReport.name()
}),
SPECTATOR(new String[] {
diff --git a/helix-core/src/main/java/org/apache/helix/SystemPropertyKeys.java b/helix-core/src/main/java/org/apache/helix/SystemPropertyKeys.java
index d316986..1a6a797 100644
--- a/helix-core/src/main/java/org/apache/helix/SystemPropertyKeys.java
+++ b/helix-core/src/main/java/org/apache/helix/SystemPropertyKeys.java
@@ -6,8 +6,6 @@ public class SystemPropertyKeys {
// ZKHelixManager
public static final String CLUSTER_MANAGER_VERSION = "cluster-manager-version.properties";
- // soft constraints weight definitions
- public static final String SOFT_CONSTRAINT_WEIGHTS = "soft-constraint-weight.properties";
public static final String FLAPPING_TIME_WINDOW = "helixmanager.flappingTimeWindow";
diff --git a/helix-core/src/main/java/org/apache/helix/controller/GenericHelixController.java b/helix-core/src/main/java/org/apache/helix/controller/GenericHelixController.java
index e47c420..39a5ad7 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/GenericHelixController.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/GenericHelixController.java
@@ -25,7 +25,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
@@ -62,8 +61,6 @@ import org.apache.helix.controller.dataproviders.WorkflowControllerDataProvider;
import org.apache.helix.controller.pipeline.AsyncWorkerType;
import org.apache.helix.controller.pipeline.Pipeline;
import org.apache.helix.controller.pipeline.PipelineRegistry;
-import org.apache.helix.controller.rebalancer.StatefulRebalancer;
-import org.apache.helix.controller.rebalancer.waged.WagedRebalancer;
import org.apache.helix.controller.stages.AttributeName;
import org.apache.helix.controller.stages.BestPossibleStateCalcStage;
import org.apache.helix.controller.stages.ClusterEvent;
@@ -171,6 +168,7 @@ public class GenericHelixController implements IdealStateChangeListener,
Timer _onDemandRebalanceTimer = null;
AtomicReference<RebalanceTask> _nextRebalanceTask = new AtomicReference<>();
+
/**
* A cache maintained across pipelines
*/
@@ -188,17 +186,6 @@ public class GenericHelixController implements IdealStateChangeListener,
private HelixManager _helixManager;
- // Since the stateful rebalancer needs to be lazily constructed when the HelixManager instance is
- // ready, the GenericHelixController is not constructed with a stateful rebalancer. This wrapper
- // is to avoid the complexity of handling a nullable value in the event handling process.
- // TODO Create the required stateful rebalancer only when it is used by any resource.
- private final StatefulRebalancerRef _rebalancerRef = new StatefulRebalancerRef() {
- @Override
- protected StatefulRebalancer createRebalancer(HelixManager helixManager) {
- return new WagedRebalancer(helixManager);
- }
- };
-
/**
* TODO: We should get rid of this once we move to:
* 1) ZK callback should go to ClusterDataCache and trigger data cache refresh only
@@ -234,29 +221,17 @@ public class GenericHelixController implements IdealStateChangeListener,
class RebalanceTask extends TimerTask {
final HelixManager _manager;
final ClusterEventType _clusterEventType;
- private final Optional<Boolean> _shouldRefreshCacheOption;
private long _nextRebalanceTime;
public RebalanceTask(HelixManager manager, ClusterEventType clusterEventType) {
this(manager, clusterEventType, -1);
- }
-
- public RebalanceTask(HelixManager manager, ClusterEventType clusterEventType,
- long nextRebalanceTime) {
- this(manager, clusterEventType, nextRebalanceTime, Optional.empty());
- }
- public RebalanceTask(HelixManager manager, ClusterEventType clusterEventType,
- long nextRebalanceTime, boolean shouldRefreshCache) {
- this(manager, clusterEventType, nextRebalanceTime, Optional.of(shouldRefreshCache));
}
- private RebalanceTask(HelixManager manager, ClusterEventType clusterEventType,
- long nextRebalanceTime, Optional<Boolean> shouldRefreshCacheOption) {
+ public RebalanceTask(HelixManager manager, ClusterEventType clusterEventType, long nextRebalanceTime) {
_manager = manager;
_clusterEventType = clusterEventType;
_nextRebalanceTime = nextRebalanceTime;
- _shouldRefreshCacheOption = shouldRefreshCacheOption;
}
public long getNextRebalanceTime() {
@@ -266,9 +241,8 @@ public class GenericHelixController implements IdealStateChangeListener,
@Override
public void run() {
try {
- if (_shouldRefreshCacheOption.orElse(
- _clusterEventType.equals(ClusterEventType.PeriodicalRebalance) || _clusterEventType
- .equals(ClusterEventType.OnDemandRebalance))) {
+ if (_clusterEventType.equals(ClusterEventType.PeriodicalRebalance) || _clusterEventType
+ .equals(ClusterEventType.OnDemandRebalance)) {
requestDataProvidersFullRefresh();
HelixDataAccessor accessor = _manager.getHelixDataAccessor();
@@ -386,17 +360,7 @@ public class GenericHelixController implements IdealStateChangeListener,
* Schedule an on demand rebalance pipeline.
* @param delay
*/
- @Deprecated
public void scheduleOnDemandRebalance(long delay) {
- scheduleOnDemandRebalance(delay, true);
- }
-
- /**
- * Schedule an on demand rebalance pipeline.
- * @param delay
- * @param shouldRefreshCache true if refresh the cache before scheduling a rebalance.
- */
- public void scheduleOnDemandRebalance(long delay, boolean shouldRefreshCache) {
if (_helixManager == null) {
logger.error("Failed to schedule a future pipeline run for cluster {}. Helix manager is null!",
_clusterName);
@@ -414,8 +378,7 @@ public class GenericHelixController implements IdealStateChangeListener,
}
RebalanceTask newTask =
- new RebalanceTask(_helixManager, ClusterEventType.OnDemandRebalance, rebalanceTime,
- shouldRefreshCache);
+ new RebalanceTask(_helixManager, ClusterEventType.OnDemandRebalance, rebalanceTime);
_onDemandRebalanceTimer.schedule(newTask, delay);
logger.info("Scheduled instant pipeline run for cluster {}." , _helixManager.getClusterName());
@@ -638,22 +601,6 @@ public class GenericHelixController implements IdealStateChangeListener,
return;
}
- // Event handling happens in a different thread from the onControllerChange processing thread.
- // Thus, there are several possible conditions.
- // 1. Event handled after leadership acquired. So we will have a valid rebalancer for the
- // event processing.
- // 2. Event handled shortly after leadership relinquished. And the rebalancer has not been
- // marked as invalid yet. So the event will be processed the same as case one.
- // 3. Event is leftover from the previous session, and it is handled when the controller
- // regains the leadership. The rebalancer will be reset before being used. That is the
- // expected behavior so as to avoid inconsistent rebalance result.
- // 4. Event handled shortly after leadership relinquished. And the rebalancer has been marked
- // as invalid. So we reset the rebalancer. But the later isLeader() check will return false and
- // the pipeline will be triggered. So the reset rebalancer won't be used before the controller
- // regains leadership.
- event.addAttribute(AttributeName.STATEFUL_REBALANCER.name(),
- _rebalancerRef.getRebalancer(manager));
-
if (!manager.isLeader()) {
logger.error("Cluster manager: " + manager.getInstanceName() + " is not leader for " + manager
.getClusterName() + ". Pipeline will not be invoked");
@@ -1050,12 +997,6 @@ public class GenericHelixController implements IdealStateChangeListener,
_clusterStatusMonitor.setMaintenance(_inMaintenanceMode);
} else {
enableClusterStatusMonitor(false);
- // Note that onControllerChange is executed in parallel with the event processing thread. It
- // is possible that the current WAGED rebalancer object is in use for handling callback. So
- // mark the rebalancer invalid only, instead of closing it here.
- // This to-be-closed WAGED rebalancer will be reset later on a later event processing if
- // the controller becomes leader again.
- _rebalancerRef.invalidateRebalancer();
}
logger.info("END: GenericClusterController.onControllerChange() for cluster " + _clusterName);
@@ -1159,8 +1100,6 @@ public class GenericHelixController implements IdealStateChangeListener,
enableClusterStatusMonitor(false);
- _rebalancerRef.closeRebalancer();
-
// TODO controller shouldn't be used in anyway after shutdown.
// Need to record shutdown and throw Exception if the controller is used again.
}
@@ -1238,6 +1177,7 @@ public class GenericHelixController implements IdealStateChangeListener,
return statusFlag;
}
+
// TODO: refactor this to use common/ClusterEventProcessor.
@Deprecated
private class ClusterEventProcessor extends Thread {
@@ -1293,59 +1233,4 @@ public class GenericHelixController implements IdealStateChangeListener,
eventThread.setDaemon(true);
eventThread.start();
}
-
- /**
- * A wrapper class for the stateful rebalancer instance that will be tracked in the
- * GenericHelixController.
- */
- private abstract class StatefulRebalancerRef<T extends StatefulRebalancer> {
- private T _rebalancer = null;
- private boolean _isRebalancerValid = true;
-
- /**
- * @param helixManager
- * @return A new stateful rebalancer instance with initial state.
- */
- protected abstract T createRebalancer(HelixManager helixManager);
-
- /**
- * Mark the current rebalancer object to be invalid, which indicates it needs to be reset before
- * the next usage.
- */
- synchronized void invalidateRebalancer() {
- _isRebalancerValid = false;
- }
-
- /**
- * @return A valid rebalancer object.
- * If the rebalancer is no longer valid, it will be reset before returning.
- * TODO: Make rebalancer volatile or make it singleton, if this method is called in multiple
- * TODO: threads outside the controller object.
- */
- synchronized T getRebalancer(HelixManager helixManager) {
- // Lazily initialize the stateful rebalancer instance since the GenericHelixController
- // instance is instantiated without the HelixManager information that is required.
- if (_rebalancer == null) {
- _rebalancer = createRebalancer(helixManager);
- _isRebalancerValid = true;
- }
- // If the rebalance exists but has been marked as invalid (due to leadership switch), it needs
- // to be reset before return.
- if (!_isRebalancerValid) {
- _rebalancer.reset();
- _isRebalancerValid = true;
- }
- return _rebalancer;
- }
-
- /**
- * Proactively close the rebalance object to release the resources.
- */
- synchronized void closeRebalancer() {
- if (_rebalancer != null) {
- _rebalancer.close();
- _rebalancer = null;
- }
- }
- }
-}
+}
\ No newline at end of file
diff --git a/helix-core/src/main/java/org/apache/helix/controller/changedetector/ChangeDetector.java b/helix-core/src/main/java/org/apache/helix/controller/changedetector/ChangeDetector.java
deleted file mode 100644
index fbe4afc..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/changedetector/ChangeDetector.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package org.apache.helix.controller.changedetector;
-
-/*
- * 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.
- */
-
-import java.util.Collection;
-import org.apache.helix.HelixConstants;
-
-/**
- * ChangeDetector interface that will be used to track deltas in the cluster from one pipeline run
- * to another. The interface methods are designed to be flexible for both the resource pipeline and
- * the task pipeline.
- * TODO: Consider splitting this up into two different ChangeDetector interfaces:
- * TODO: PropertyBasedChangeDetector and PathBasedChangeDetector.
- */
-public interface ChangeDetector {
-
- /**
- * Returns all types of changes detected.
- * @return a collection of ChangeTypes
- */
- Collection<HelixConstants.ChangeType> getChangeTypes();
-
- /**
- * Returns the names of items that changed based on the change type given.
- * @return a collection of names of items that changed
- */
- Collection<String> getChangesByType(HelixConstants.ChangeType changeType);
-
- /**
- * Returns the names of items that were added based on the change type given.
- * @return a collection of names of items that were added
- */
- Collection<String> getAdditionsByType(HelixConstants.ChangeType changeType);
-
- /**
- * Returns the names of items that were removed based on the change type given.
- * @return a collection of names of items that were removed
- */
- Collection<String> getRemovalsByType(HelixConstants.ChangeType changeType);
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeDetector.java b/helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeDetector.java
deleted file mode 100644
index 27f4c50..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeDetector.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package org.apache.helix.controller.changedetector;
-
-/*
- * 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.
- */
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.google.common.collect.Sets;
-import org.apache.helix.HelixConstants;
-import org.apache.helix.HelixProperty;
-import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
-import org.apache.helix.model.ClusterConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * ResourceChangeDetector implements ChangeDetector. It caches resource-related metadata from
- * Helix's main resource pipeline cache (DataProvider) and the computation results of change
- * detection.
- * WARNING: the methods of this class are not thread-safe.
- */
-public class ResourceChangeDetector implements ChangeDetector {
- private static final Logger LOG = LoggerFactory.getLogger(ResourceChangeDetector.class.getName());
-
- private final boolean _ignoreControllerGeneratedFields;
- private ResourceChangeSnapshot _oldSnapshot; // snapshot for previous pipeline run
- private ResourceChangeSnapshot _newSnapshot; // snapshot for this pipeline run
-
- // The following caches the computation results
- private Map<HelixConstants.ChangeType, Collection<String>> _changedItems = new HashMap<>();
- private Map<HelixConstants.ChangeType, Collection<String>> _addedItems = new HashMap<>();
- private Map<HelixConstants.ChangeType, Collection<String>> _removedItems = new HashMap<>();
-
- public ResourceChangeDetector(boolean ignoreControllerGeneratedFields) {
- _newSnapshot = new ResourceChangeSnapshot();
- _ignoreControllerGeneratedFields = ignoreControllerGeneratedFields;
- }
-
- public ResourceChangeDetector() {
- this(false);
- }
-
- /**
- * Compare the underlying HelixProperty objects and produce a collection of names of changed
- * properties.
- * @return
- */
- private Collection<String> getChangedItems(Map<String, ? extends HelixProperty> oldPropertyMap,
- Map<String, ? extends HelixProperty> newPropertyMap) {
- Collection<String> changedItems = new HashSet<>();
- oldPropertyMap.forEach((name, property) -> {
- if (newPropertyMap.containsKey(name)
- && !property.getRecord().equals(newPropertyMap.get(name).getRecord())) {
- changedItems.add(name);
- }
- });
- return changedItems;
- }
-
- /**
- * Return a collection of names that are newly added.
- * @return
- */
- private Collection<String> getAddedItems(Map<String, ? extends HelixProperty> oldPropertyMap,
- Map<String, ? extends HelixProperty> newPropertyMap) {
- return Sets.difference(newPropertyMap.keySet(), oldPropertyMap.keySet());
- }
-
- /**
- * Return a collection of names that were removed.
- * @return
- */
- private Collection<String> getRemovedItems(Map<String, ? extends HelixProperty> oldPropertyMap,
- Map<String, ? extends HelixProperty> newPropertyMap) {
- return Sets.difference(oldPropertyMap.keySet(), newPropertyMap.keySet());
- }
-
- private void clearCachedComputation() {
- _changedItems.clear();
- _addedItems.clear();
- _removedItems.clear();
- }
-
- /**
- * Based on the change type given and propertyMap type, call the right getters for propertyMap.
- * @param changeType
- * @param snapshot
- * @return
- */
- private Map<String, ? extends HelixProperty> determinePropertyMapByType(
- HelixConstants.ChangeType changeType, ResourceChangeSnapshot snapshot) {
- switch (changeType) {
- case INSTANCE_CONFIG:
- return snapshot.getInstanceConfigMap();
- case IDEAL_STATE:
- return snapshot.getIdealStateMap();
- case RESOURCE_CONFIG:
- return snapshot.getResourceConfigMap();
- case LIVE_INSTANCE:
- return snapshot.getLiveInstances();
- case CLUSTER_CONFIG:
- ClusterConfig config = snapshot.getClusterConfig();
- if (config == null) {
- return Collections.emptyMap();
- } else {
- return Collections.singletonMap(config.getClusterName(), config);
- }
- default:
- LOG.warn(
- "ResourceChangeDetector cannot determine propertyMap for the given ChangeType: {}. Returning an empty map.",
- changeType);
- return Collections.emptyMap();
- }
- }
-
- /**
- * Makes the current newSnapshot the oldSnapshot and reads in the up-to-date snapshot for change
- * computation. To be called in the controller pipeline.
- * @param dataProvider newly refreshed DataProvider (cache)
- */
- public synchronized void updateSnapshots(ResourceControllerDataProvider dataProvider) {
- // If there are changes, update internal states
- _oldSnapshot = new ResourceChangeSnapshot(_newSnapshot);
- _newSnapshot = new ResourceChangeSnapshot(dataProvider, _ignoreControllerGeneratedFields);
- dataProvider.clearRefreshedChangeTypes();
-
- // Invalidate cached computation
- clearCachedComputation();
- }
-
- public synchronized void resetSnapshots() {
- _newSnapshot = new ResourceChangeSnapshot();
- clearCachedComputation();
- }
-
- @Override
- public synchronized Collection<HelixConstants.ChangeType> getChangeTypes() {
- return Collections.unmodifiableSet(_newSnapshot.getChangedTypes());
- }
-
- @Override
- public synchronized Collection<String> getChangesByType(HelixConstants.ChangeType changeType) {
- return _changedItems.computeIfAbsent(changeType,
- changedItems -> getChangedItems(determinePropertyMapByType(changeType, _oldSnapshot),
- determinePropertyMapByType(changeType, _newSnapshot)));
- }
-
- @Override
- public synchronized Collection<String> getAdditionsByType(HelixConstants.ChangeType changeType) {
- return _addedItems.computeIfAbsent(changeType,
- changedItems -> getAddedItems(determinePropertyMapByType(changeType, _oldSnapshot),
- determinePropertyMapByType(changeType, _newSnapshot)));
- }
-
- @Override
- public synchronized Collection<String> getRemovalsByType(HelixConstants.ChangeType changeType) {
- return _removedItems.computeIfAbsent(changeType,
- changedItems -> getRemovedItems(determinePropertyMapByType(changeType, _oldSnapshot),
- determinePropertyMapByType(changeType, _newSnapshot)));
- }
-
- /**
- * @return A map contains all the changed items that are categorized by the change types.
- */
- public Map<HelixConstants.ChangeType, Set<String>> getAllChanges() {
- return getChangeTypes().stream()
- .collect(Collectors.toMap(changeType -> changeType, changeType -> {
- Set<String> itemKeys = new HashSet<>();
- itemKeys.addAll(getAdditionsByType(changeType));
- itemKeys.addAll(getChangesByType(changeType));
- itemKeys.addAll(getRemovalsByType(changeType));
- return itemKeys;
- })).entrySet().stream().filter(changeEntry -> !changeEntry.getValue().isEmpty()).collect(
- Collectors
- .toMap(changeEntry -> changeEntry.getKey(), changeEntry -> changeEntry.getValue()));
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeSnapshot.java b/helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeSnapshot.java
deleted file mode 100644
index fc8c5c4..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/changedetector/ResourceChangeSnapshot.java
+++ /dev/null
@@ -1,157 +0,0 @@
-package org.apache.helix.controller.changedetector;
-
-/*
- * 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.
- */
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apache.helix.HelixConstants;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
-import org.apache.helix.model.ClusterConfig;
-import org.apache.helix.model.IdealState;
-import org.apache.helix.model.InstanceConfig;
-import org.apache.helix.model.LiveInstance;
-import org.apache.helix.model.ResourceConfig;
-
-/**
- * ResourceChangeSnapshot is a POJO that contains the following Helix metadata:
- * 1. InstanceConfig
- * 2. IdealState
- * 3. ResourceConfig
- * 4. LiveInstance
- * 5. Changed property types
- * It serves as a snapshot of the main controller cache to enable the difference (change)
- * calculation between two rounds of the pipeline run.
- */
-class ResourceChangeSnapshot {
-
- private Set<HelixConstants.ChangeType> _changedTypes;
- private Map<String, InstanceConfig> _instanceConfigMap;
- private Map<String, IdealState> _idealStateMap;
- private Map<String, ResourceConfig> _resourceConfigMap;
- private Map<String, LiveInstance> _liveInstances;
- private ClusterConfig _clusterConfig;
-
- /**
- * Default constructor that constructs an empty snapshot.
- */
- ResourceChangeSnapshot() {
- _changedTypes = new HashSet<>();
- _instanceConfigMap = new HashMap<>();
- _idealStateMap = new HashMap<>();
- _resourceConfigMap = new HashMap<>();
- _liveInstances = new HashMap<>();
- _clusterConfig = null;
- }
-
- /**
- * Constructor using controller cache (ResourceControllerDataProvider).
- *
- * @param dataProvider
- * @param ignoreControllerGeneratedFields if true, the snapshot won't record any changes that is
- * being modified by the controller.
- */
- ResourceChangeSnapshot(ResourceControllerDataProvider dataProvider,
- boolean ignoreControllerGeneratedFields) {
- _changedTypes = new HashSet<>(dataProvider.getRefreshedChangeTypes());
- _instanceConfigMap = new HashMap<>(dataProvider.getInstanceConfigMap());
- _idealStateMap = new HashMap<>(dataProvider.getIdealStates());
- if (ignoreControllerGeneratedFields && (
- dataProvider.getClusterConfig().isPersistBestPossibleAssignment() || dataProvider
- .getClusterConfig().isPersistIntermediateAssignment())) {
- for (String resourceName : _idealStateMap.keySet()) {
- _idealStateMap.put(resourceName, trimIdealState(_idealStateMap.get(resourceName)));
- }
- }
- _resourceConfigMap = new HashMap<>(dataProvider.getResourceConfigMap());
- _liveInstances = new HashMap<>(dataProvider.getLiveInstances());
- _clusterConfig = dataProvider.getClusterConfig();
- }
-
- /**
- * Copy constructor for ResourceChangeCache.
- * @param snapshot
- */
- ResourceChangeSnapshot(ResourceChangeSnapshot snapshot) {
- _changedTypes = new HashSet<>(snapshot._changedTypes);
- _instanceConfigMap = new HashMap<>(snapshot._instanceConfigMap);
- _idealStateMap = new HashMap<>(snapshot._idealStateMap);
- _resourceConfigMap = new HashMap<>(snapshot._resourceConfigMap);
- _liveInstances = new HashMap<>(snapshot._liveInstances);
- _clusterConfig = snapshot._clusterConfig;
- }
-
- Set<HelixConstants.ChangeType> getChangedTypes() {
- return _changedTypes;
- }
-
- Map<String, InstanceConfig> getInstanceConfigMap() {
- return _instanceConfigMap;
- }
-
- Map<String, IdealState> getIdealStateMap() {
- return _idealStateMap;
- }
-
- Map<String, ResourceConfig> getResourceConfigMap() {
- return _resourceConfigMap;
- }
-
- Map<String, LiveInstance> getLiveInstances() {
- return _liveInstances;
- }
-
- ClusterConfig getClusterConfig() {
- return _clusterConfig;
- }
-
- // Trim the IdealState to exclude any controller modified information.
- private IdealState trimIdealState(IdealState originalIdealState) {
- // Clone the IdealState to avoid modifying the objects in the Cluster Data Cache, which might
- // be used by the other stages in the pipeline.
- IdealState trimmedIdealState = new IdealState(originalIdealState.getRecord());
- ZNRecord trimmedIdealStateRecord = trimmedIdealState.getRecord();
- switch (originalIdealState.getRebalanceMode()) {
- // WARNING: the IdealState copy constructor is not really deep copy. So we should not modify
- // the values directly or the cached values will be changed.
- case FULL_AUTO:
- // For FULL_AUTO resources, both map fields and list fields are not considered as data input
- // for the controller. The controller will write to these two types of fields for persisting
- // the assignment mapping.
- trimmedIdealStateRecord.setListFields(trimmedIdealStateRecord.getListFields().keySet().stream().collect(
- Collectors.toMap(partition -> partition, partition -> Collections.emptyList())));
- // Continue to clean up map fields in the SEMI_AUTO case.
- case SEMI_AUTO:
- // For SEMI_AUTO resources, map fields are not considered as data input for the controller.
- // The controller will write to the map fields for persisting the assignment mapping.
- trimmedIdealStateRecord.setMapFields(trimmedIdealStateRecord.getMapFields().keySet().stream().collect(
- Collectors.toMap(partition -> partition, partition -> Collections.emptyMap())));
- break;
- default:
- break;
- }
- return trimmedIdealState;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/dataproviders/ResourceControllerDataProvider.java b/helix-core/src/main/java/org/apache/helix/controller/dataproviders/ResourceControllerDataProvider.java
index 1631d50..b1dc215 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/dataproviders/ResourceControllerDataProvider.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/dataproviders/ResourceControllerDataProvider.java
@@ -25,7 +25,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
import org.apache.helix.HelixConstants;
import org.apache.helix.HelixDataAccessor;
import org.apache.helix.PropertyKey;
@@ -65,9 +64,6 @@ public class ResourceControllerDataProvider extends BaseControllerDataProvider {
private Map<String, Map<String, MissingTopStateRecord>> _missingTopStateMap;
private Map<String, Map<String, String>> _lastTopStateLocationMap;
- // Maintain a set of all ChangeTypes for change detection
- private Set<HelixConstants.ChangeType> _refreshedChangeTypes;
-
public ResourceControllerDataProvider() {
this(AbstractDataCache.UNKNOWN_CLUSTER);
}
@@ -110,22 +106,19 @@ public class ResourceControllerDataProvider extends BaseControllerDataProvider {
_idealMappingCache = new HashMap<>();
_missingTopStateMap = new HashMap<>();
_lastTopStateLocationMap = new HashMap<>();
- _refreshedChangeTypes = ConcurrentHashMap.newKeySet();
}
public synchronized void refresh(HelixDataAccessor accessor) {
long startTime = System.currentTimeMillis();
// Refresh base
- Set<HelixConstants.ChangeType> changedTypes = super.doRefresh(accessor);
- _refreshedChangeTypes.addAll(changedTypes);
+ Set<HelixConstants.ChangeType> propertyRefreshed = super.doRefresh(accessor);
// Invalidate cached information if any of the important data has been refreshed
- if (changedTypes.contains(HelixConstants.ChangeType.IDEAL_STATE)
- || changedTypes.contains(HelixConstants.ChangeType.LIVE_INSTANCE)
- || changedTypes.contains(HelixConstants.ChangeType.INSTANCE_CONFIG)
- || changedTypes.contains(HelixConstants.ChangeType.RESOURCE_CONFIG)
- || changedTypes.contains((HelixConstants.ChangeType.CLUSTER_CONFIG))) {
+ if (propertyRefreshed.contains(HelixConstants.ChangeType.IDEAL_STATE)
+ || propertyRefreshed.contains(HelixConstants.ChangeType.LIVE_INSTANCE)
+ || propertyRefreshed.contains(HelixConstants.ChangeType.INSTANCE_CONFIG)
+ || propertyRefreshed.contains(HelixConstants.ChangeType.RESOURCE_CONFIG)) {
clearCachedResourceAssignments();
}
@@ -268,23 +261,6 @@ public class ResourceControllerDataProvider extends BaseControllerDataProvider {
_idealMappingCache.put(resource, mapping);
}
- /**
- * Return the set of all PropertyTypes that changed prior to this round of rebalance. The caller
- * should clear this set by calling {@link #clearRefreshedChangeTypes()}.
- * @return
- */
- public Set<HelixConstants.ChangeType> getRefreshedChangeTypes() {
- return _refreshedChangeTypes;
- }
-
- /**
- * Clears the set of all PropertyTypes that changed. The caller will have consumed all change
- * types by calling {@link #getRefreshedChangeTypes()}.
- */
- public void clearRefreshedChangeTypes() {
- _refreshedChangeTypes.clear();
- }
-
public void clearCachedResourceAssignments() {
_resourceAssignmentCache.clear();
_idealMappingCache.clear();
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/DelayedAutoRebalancer.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/DelayedAutoRebalancer.java
index 63870ec..6ae7076 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/DelayedAutoRebalancer.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/DelayedAutoRebalancer.java
@@ -33,10 +33,11 @@ import org.apache.helix.HelixDefinedState;
import org.apache.helix.ZNRecord;
import org.apache.helix.api.config.StateTransitionThrottleConfig;
import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
-import org.apache.helix.controller.rebalancer.util.DelayedRebalanceUtil;
+import org.apache.helix.controller.rebalancer.util.RebalanceScheduler;
import org.apache.helix.controller.stages.CurrentStateOutput;
import org.apache.helix.model.ClusterConfig;
import org.apache.helix.model.IdealState;
+import org.apache.helix.model.InstanceConfig;
import org.apache.helix.model.Partition;
import org.apache.helix.model.Resource;
import org.apache.helix.model.ResourceAssignment;
@@ -50,6 +51,7 @@ import org.slf4j.LoggerFactory;
*/
public class DelayedAutoRebalancer extends AbstractRebalancer<ResourceControllerDataProvider> {
private static final Logger LOG = LoggerFactory.getLogger(DelayedAutoRebalancer.class);
+ private static RebalanceScheduler _rebalanceScheduler = new RebalanceScheduler();
@Override
public IdealState computeNewIdealState(String resourceName,
@@ -78,8 +80,7 @@ public class DelayedAutoRebalancer extends AbstractRebalancer<ResourceController
ClusterConfig clusterConfig = clusterData.getClusterConfig();
ResourceConfig resourceConfig = clusterData.getResourceConfig(resourceName);
- boolean delayRebalanceEnabled =
- DelayedRebalanceUtil.isDelayRebalanceEnabled(currentIdealState, clusterConfig);
+ boolean delayRebalanceEnabled = isDelayRebalanceEnabled(currentIdealState, clusterConfig);
if (resourceConfig != null) {
userDefinedPreferenceList = resourceConfig.getPreferenceLists();
@@ -110,18 +111,16 @@ public class DelayedAutoRebalancer extends AbstractRebalancer<ResourceController
Set<String> activeNodes = liveEnabledNodes;
if (delayRebalanceEnabled) {
- long delay = DelayedRebalanceUtil.getRebalanceDelay(currentIdealState, clusterConfig);
- activeNodes = DelayedRebalanceUtil
- .getActiveNodes(allNodes, currentIdealState, liveEnabledNodes,
- clusterData.getInstanceOfflineTimeMap(), clusterData.getLiveInstances().keySet(),
- clusterData.getInstanceConfigMap(), delay, clusterConfig);
+ long delay = getRebalanceDelay(currentIdealState, clusterConfig);
+ activeNodes = getActiveInstances(allNodes, currentIdealState, liveEnabledNodes,
+ clusterData.getInstanceOfflineTimeMap(), clusterData.getLiveInstances().keySet(),
+ clusterData.getInstanceConfigMap(), delay, clusterConfig);
Set<String> offlineOrDisabledInstances = new HashSet<>(activeNodes);
offlineOrDisabledInstances.removeAll(liveEnabledNodes);
- DelayedRebalanceUtil.setRebalanceScheduler(currentIdealState.getResourceName(), true,
- offlineOrDisabledInstances, clusterData.getInstanceOfflineTimeMap(),
- clusterData.getLiveInstances().keySet(), clusterData.getInstanceConfigMap(), delay,
- clusterConfig, _manager);
+ setRebalanceScheduler(currentIdealState, offlineOrDisabledInstances,
+ clusterData.getInstanceOfflineTimeMap(), clusterData.getLiveInstances().keySet(),
+ clusterData.getInstanceConfigMap(), delay, clusterConfig);
}
if (allNodes.isEmpty() || activeNodes.isEmpty()) {
@@ -164,16 +163,16 @@ public class DelayedAutoRebalancer extends AbstractRebalancer<ResourceController
.computePartitionAssignment(allNodeList, liveEnabledNodeList, currentMapping, clusterData);
ZNRecord finalMapping = newIdealMapping;
- if (DelayedRebalanceUtil.isDelayRebalanceEnabled(currentIdealState, clusterConfig)) {
+ if (isDelayRebalanceEnabled(currentIdealState, clusterConfig)) {
List<String> activeNodeList = new ArrayList<>(activeNodes);
Collections.sort(activeNodeList);
- int minActiveReplicas =
- DelayedRebalanceUtil.getMinActiveReplica(currentIdealState, replicaCount);
+ int minActiveReplicas = getMinActiveReplica(currentIdealState, replicaCount);
ZNRecord newActiveMapping = _rebalanceStrategy
.computePartitionAssignment(allNodeList, activeNodeList, currentMapping, clusterData);
- finalMapping = getFinalDelayedMapping(currentIdealState, newIdealMapping, newActiveMapping,
- liveEnabledNodes, replicaCount, minActiveReplicas);
+ finalMapping =
+ getFinalDelayedMapping(currentIdealState, newIdealMapping, newActiveMapping, liveEnabledNodes,
+ replicaCount, minActiveReplicas);
}
finalMapping.getListFields().putAll(userDefinedPreferenceList);
@@ -204,15 +203,162 @@ public class DelayedAutoRebalancer extends AbstractRebalancer<ResourceController
return newIdealState;
}
+ /* get all active instances (live instances plus offline-yet-active instances */
+ private Set<String> getActiveInstances(Set<String> allNodes, IdealState idealState,
+ Set<String> liveEnabledNodes, Map<String, Long> instanceOfflineTimeMap, Set<String> liveNodes,
+ Map<String, InstanceConfig> instanceConfigMap, long delay, ClusterConfig clusterConfig) {
+ Set<String> activeInstances = new HashSet<>(liveEnabledNodes);
+
+ if (!isDelayRebalanceEnabled(idealState, clusterConfig)) {
+ return activeInstances;
+ }
+
+ Set<String> offlineOrDisabledInstances = new HashSet<>(allNodes);
+ offlineOrDisabledInstances.removeAll(liveEnabledNodes);
+
+ long currentTime = System.currentTimeMillis();
+ for (String ins : offlineOrDisabledInstances) {
+ long inactiveTime = getInactiveTime(ins, liveNodes, instanceOfflineTimeMap.get(ins), delay,
+ instanceConfigMap.get(ins), clusterConfig);
+ InstanceConfig instanceConfig = instanceConfigMap.get(ins);
+ if (inactiveTime > currentTime && instanceConfig != null && instanceConfig
+ .isDelayRebalanceEnabled()) {
+ activeInstances.add(ins);
+ }
+ }
+
+ return activeInstances;
+ }
+
+ /* Set a rebalance scheduler for the closest future rebalance time. */
+ private void setRebalanceScheduler(IdealState idealState, Set<String> offlineOrDisabledInstances,
+ Map<String, Long> instanceOfflineTimeMap, Set<String> liveNodes,
+ Map<String, InstanceConfig> instanceConfigMap, long delay,
+ ClusterConfig clusterConfig) {
+ String resourceName = idealState.getResourceName();
+ if (!isDelayRebalanceEnabled(idealState, clusterConfig)) {
+ _rebalanceScheduler.removeScheduledRebalance(resourceName);
+ return;
+ }
+
+ long currentTime = System.currentTimeMillis();
+ long nextRebalanceTime = Long.MAX_VALUE;
+ // calculate the closest future rebalance time
+ for (String ins : offlineOrDisabledInstances) {
+ long inactiveTime = getInactiveTime(ins, liveNodes, instanceOfflineTimeMap.get(ins), delay,
+ instanceConfigMap.get(ins), clusterConfig);
+ if (inactiveTime != -1 && inactiveTime > currentTime && inactiveTime < nextRebalanceTime) {
+ nextRebalanceTime = inactiveTime;
+ }
+ }
+
+ if (nextRebalanceTime == Long.MAX_VALUE) {
+ long startTime = _rebalanceScheduler.removeScheduledRebalance(resourceName);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String
+ .format("Remove exist rebalance timer for resource %s at %d\n", resourceName, startTime));
+ }
+ } else {
+ long currentScheduledTime = _rebalanceScheduler.getRebalanceTime(resourceName);
+ if (currentScheduledTime < 0 || currentScheduledTime > nextRebalanceTime) {
+ _rebalanceScheduler.scheduleRebalance(_manager, resourceName, nextRebalanceTime);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(String
+ .format("Set next rebalance time for resource %s at time %d\n", resourceName,
+ nextRebalanceTime));
+ }
+ }
+ }
+ }
+
+ /**
+ * The time when an offline or disabled instance should be treated as inactive. return -1 if it is
+ * inactive now.
+ *
+ * @return
+ */
+ private long getInactiveTime(String instance, Set<String> liveInstances, Long offlineTime,
+ long delay, InstanceConfig instanceConfig, ClusterConfig clusterConfig) {
+ long inactiveTime = Long.MAX_VALUE;
+
+ // check the time instance went offline.
+ if (!liveInstances.contains(instance)) {
+ if (offlineTime != null && offlineTime > 0 && offlineTime + delay < inactiveTime) {
+ inactiveTime = offlineTime + delay;
+ }
+ }
+
+ // check the time instance got disabled.
+ if (!instanceConfig.getInstanceEnabled() || (clusterConfig.getDisabledInstances() != null
+ && clusterConfig.getDisabledInstances().containsKey(instance))) {
+ long disabledTime = instanceConfig.getInstanceEnabledTime();
+ if (clusterConfig.getDisabledInstances() != null && clusterConfig.getDisabledInstances()
+ .containsKey(instance)) {
+ // Update batch disable time
+ long batchDisableTime = Long.parseLong(clusterConfig.getDisabledInstances().get(instance));
+ if (disabledTime == -1 || disabledTime > batchDisableTime) {
+ disabledTime = batchDisableTime;
+ }
+ }
+ if (disabledTime > 0 && disabledTime + delay < inactiveTime) {
+ inactiveTime = disabledTime + delay;
+ }
+ }
+
+ if (inactiveTime == Long.MAX_VALUE) {
+ return -1;
+ }
+
+ return inactiveTime;
+ }
+
+ private long getRebalanceDelay(IdealState idealState, ClusterConfig clusterConfig) {
+ long delayTime = idealState.getRebalanceDelay();
+ if (delayTime < 0) {
+ delayTime = clusterConfig.getRebalanceDelayTime();
+ }
+ return delayTime;
+ }
+
+ private boolean isDelayRebalanceEnabled(IdealState idealState, ClusterConfig clusterConfig) {
+ long delay = getRebalanceDelay(idealState, clusterConfig);
+ return (delay > 0 && idealState.isDelayRebalanceEnabled() && clusterConfig
+ . isDelayRebalaceEnabled());
+ }
+
private ZNRecord getFinalDelayedMapping(IdealState idealState, ZNRecord newIdealMapping,
ZNRecord newActiveMapping, Set<String> liveInstances, int numReplica, int minActiveReplica) {
if (minActiveReplica >= numReplica) {
return newIdealMapping;
}
ZNRecord finalMapping = new ZNRecord(idealState.getResourceName());
- finalMapping.setListFields(DelayedRebalanceUtil
- .getFinalDelayedMapping(newIdealMapping.getListFields(), newActiveMapping.getListFields(),
- liveInstances, minActiveReplica));
+ for (String partition : newIdealMapping.getListFields().keySet()) {
+ List<String> idealList = newIdealMapping.getListField(partition);
+ List<String> activeList = newActiveMapping.getListField(partition);
+
+ List<String> liveList = new ArrayList<>();
+ int activeReplica = 0;
+ for (String ins : activeList) {
+ if (liveInstances.contains(ins)) {
+ activeReplica++;
+ liveList.add(ins);
+ }
+ }
+
+ if (activeReplica >= minActiveReplica) {
+ finalMapping.setListField(partition, activeList);
+ } else {
+ List<String> candidates = new ArrayList<String>(idealList);
+ candidates.removeAll(activeList);
+ for (String liveIns : candidates) {
+ liveList.add(liveIns);
+ if (liveList.size() >= minActiveReplica) {
+ break;
+ }
+ }
+ finalMapping.setListField(partition, liveList);
+ }
+ }
return finalMapping;
}
@@ -246,11 +392,10 @@ public class DelayedAutoRebalancer extends AbstractRebalancer<ResourceController
Set<String> liveNodes = cache.getLiveInstances().keySet();
ClusterConfig clusterConfig = cache.getClusterConfig();
- long delayTime = DelayedRebalanceUtil.getRebalanceDelay(idealState, clusterConfig);
- Set<String> activeNodes = DelayedRebalanceUtil
- .getActiveNodes(allNodes, idealState, liveNodes, cache.getInstanceOfflineTimeMap(),
- cache.getLiveInstances().keySet(), cache.getInstanceConfigMap(), delayTime,
- clusterConfig);
+ long delayTime = getRebalanceDelay(idealState, clusterConfig);
+ Set<String> activeNodes = getActiveInstances(allNodes, idealState, liveNodes,
+ cache.getInstanceOfflineTimeMap(), cache.getLiveInstances().keySet(),
+ cache.getInstanceConfigMap(), delayTime, clusterConfig);
String stateModelDefName = idealState.getStateModelDefRef();
StateModelDefinition stateModelDef = cache.getStateModelDef(stateModelDefName);
@@ -275,6 +420,14 @@ public class DelayedAutoRebalancer extends AbstractRebalancer<ResourceController
return partitionMapping;
}
+ private int getMinActiveReplica(IdealState idealState, int replicaCount) {
+ int minActiveReplicas = idealState.getMinActiveReplicas();
+ if (minActiveReplicas < 0) {
+ minActiveReplicas = replicaCount;
+ }
+ return minActiveReplicas;
+ }
+
/**
* compute best state for resource in AUTO ideal state mode
* @param liveInstances
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/StatefulRebalancer.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/StatefulRebalancer.java
deleted file mode 100644
index 94567bb..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/StatefulRebalancer.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.apache.helix.controller.rebalancer;
-
-import java.util.Map;
-
-import org.apache.helix.HelixRebalanceException;
-import org.apache.helix.controller.dataproviders.BaseControllerDataProvider;
-import org.apache.helix.controller.stages.CurrentStateOutput;
-import org.apache.helix.model.IdealState;
-import org.apache.helix.model.Resource;
-
-
-/**
- * Allows one to come up with custom implementation of a stateful rebalancer.<br/>
- */
-public interface StatefulRebalancer<T extends BaseControllerDataProvider> {
-
- /**
- * Reset the rebalancer to the initial state.
- */
- void reset();
-
- /**
- * Release all the resources and clean up all the rebalancer state.
- */
- void close();
-
- /**
- * Compute the new IdealStates for all the input resources. The IdealStates include both new
- * partition assignment (in the listFiles) and the new replica state mapping (in the mapFields).
- * @param clusterData The Cluster status data provider.
- * @param resourceMap A map containing all the rebalancing resources.
- * @param currentStateOutput The present Current States of the resources.
- * @return A map of the new IdealStates with the resource name as key.
- */
- Map<String, IdealState> computeNewIdealStates(T clusterData, Map<String, Resource> resourceMap,
- final CurrentStateOutput currentStateOutput) throws HelixRebalanceException;
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/DelayedRebalanceUtil.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/DelayedRebalanceUtil.java
deleted file mode 100644
index 1342860..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/DelayedRebalanceUtil.java
+++ /dev/null
@@ -1,267 +0,0 @@
-package org.apache.helix.controller.rebalancer.util;
-
-/*
- * 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.
- */
-
-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 org.apache.helix.HelixManager;
-import org.apache.helix.model.ClusterConfig;
-import org.apache.helix.model.IdealState;
-import org.apache.helix.model.InstanceConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-/**
- * The util for supporting delayed rebalance logic.
- */
-public class DelayedRebalanceUtil {
- private static final Logger LOG = LoggerFactory.getLogger(DelayedRebalanceUtil.class);
-
- private static RebalanceScheduler REBALANCE_SCHEDULER = new RebalanceScheduler();
-
- /**
- * @return true if delay rebalance is configured and enabled in the ClusterConfig configurations.
- */
- public static boolean isDelayRebalanceEnabled(ClusterConfig clusterConfig) {
- long delay = clusterConfig.getRebalanceDelayTime();
- return (delay > 0 && clusterConfig.isDelayRebalaceEnabled());
- }
-
- /**
- * @return true if delay rebalance is configured and enabled in Resource IdealState and the
- * ClusterConfig configurations.
- */
- public static boolean isDelayRebalanceEnabled(IdealState idealState,
- ClusterConfig clusterConfig) {
- long delay = getRebalanceDelay(idealState, clusterConfig);
- return (delay > 0 && idealState.isDelayRebalanceEnabled() && clusterConfig
- .isDelayRebalaceEnabled());
- }
-
- /**
- * @return the rebalance delay based on Resource IdealState and the ClusterConfig configurations.
- */
- public static long getRebalanceDelay(IdealState idealState, ClusterConfig clusterConfig) {
- long delayTime = idealState.getRebalanceDelay();
- if (delayTime < 0) {
- delayTime = clusterConfig.getRebalanceDelayTime();
- }
- return delayTime;
- }
-
- /**
- * @return all active nodes (live nodes plus offline-yet-active nodes) while considering cluster
- * delay rebalance configurations.
- */
- public static Set<String> getActiveNodes(Set<String> allNodes, Set<String> liveEnabledNodes,
- Map<String, Long> instanceOfflineTimeMap, Set<String> liveNodes,
- Map<String, InstanceConfig> instanceConfigMap, ClusterConfig clusterConfig) {
- if (!isDelayRebalanceEnabled(clusterConfig)) {
- return new HashSet<>(liveEnabledNodes);
- }
- return getActiveNodes(allNodes, liveEnabledNodes, instanceOfflineTimeMap, liveNodes,
- instanceConfigMap, clusterConfig.getRebalanceDelayTime(), clusterConfig);
- }
-
- /**
- * @return all active nodes (live nodes plus offline-yet-active nodes) while considering cluster
- * and the resource delay rebalance configurations.
- */
- public static Set<String> getActiveNodes(Set<String> allNodes, IdealState idealState,
- Set<String> liveEnabledNodes, Map<String, Long> instanceOfflineTimeMap, Set<String> liveNodes,
- Map<String, InstanceConfig> instanceConfigMap, long delay, ClusterConfig clusterConfig) {
- if (!isDelayRebalanceEnabled(idealState, clusterConfig)) {
- return new HashSet<>(liveEnabledNodes);
- }
- return getActiveNodes(allNodes, liveEnabledNodes, instanceOfflineTimeMap, liveNodes,
- instanceConfigMap, delay, clusterConfig);
- }
-
- private static Set<String> getActiveNodes(Set<String> allNodes, Set<String> liveEnabledNodes,
- Map<String, Long> instanceOfflineTimeMap, Set<String> liveNodes,
- Map<String, InstanceConfig> instanceConfigMap, long delay, ClusterConfig clusterConfig) {
- Set<String> activeNodes = new HashSet<>(liveEnabledNodes);
- Set<String> offlineOrDisabledInstances = new HashSet<>(allNodes);
- offlineOrDisabledInstances.removeAll(liveEnabledNodes);
- long currentTime = System.currentTimeMillis();
- for (String ins : offlineOrDisabledInstances) {
- long inactiveTime = getInactiveTime(ins, liveNodes, instanceOfflineTimeMap.get(ins), delay,
- instanceConfigMap.get(ins), clusterConfig);
- InstanceConfig instanceConfig = instanceConfigMap.get(ins);
- if (inactiveTime > currentTime && instanceConfig != null && instanceConfig
- .isDelayRebalanceEnabled()) {
- activeNodes.add(ins);
- }
- }
- return activeNodes;
- }
-
- /**
- * @return The time when an offline or disabled instance should be treated as inactive.
- * Return -1 if it is inactive now.
- */
- private static long getInactiveTime(String instance, Set<String> liveInstances, Long offlineTime,
- long delay, InstanceConfig instanceConfig, ClusterConfig clusterConfig) {
- long inactiveTime = Long.MAX_VALUE;
-
- // check the time instance went offline.
- if (!liveInstances.contains(instance)) {
- if (offlineTime != null && offlineTime > 0 && offlineTime + delay < inactiveTime) {
- inactiveTime = offlineTime + delay;
- }
- }
-
- // check the time instance got disabled.
- if (!instanceConfig.getInstanceEnabled() || (clusterConfig.getDisabledInstances() != null
- && clusterConfig.getDisabledInstances().containsKey(instance))) {
- long disabledTime = instanceConfig.getInstanceEnabledTime();
- if (clusterConfig.getDisabledInstances() != null && clusterConfig.getDisabledInstances()
- .containsKey(instance)) {
- // Update batch disable time
- long batchDisableTime = Long.parseLong(clusterConfig.getDisabledInstances().get(instance));
- if (disabledTime == -1 || disabledTime > batchDisableTime) {
- disabledTime = batchDisableTime;
- }
- }
- if (disabledTime > 0 && disabledTime + delay < inactiveTime) {
- inactiveTime = disabledTime + delay;
- }
- }
-
- if (inactiveTime == Long.MAX_VALUE) {
- return -1;
- }
-
- return inactiveTime;
- }
-
- /**
- * Merge the new ideal preference list with the delayed mapping that is calculated based on the
- * delayed rebalance configurations.
- * The method will prioritize the "active" preference list so as to avoid unnecessary transient
- * state transitions.
- *
- * @param newIdealPreferenceList the ideal mapping that was calculated based on the current
- * instance status
- * @param newDelayedPreferenceList the delayed mapping that was calculated based on the delayed
- * instance status
- * @param liveEnabledInstances list of all the nodes that are both alive and enabled.
- * @param minActiveReplica the minimum replica count to ensure a valid mapping.
- * If the active list does not have enough replica assignment,
- * this method will fill the list with the new ideal mapping until
- * the replica count satisfies the minimum requirement.
- * @return the merged state mapping.
- */
- public static Map<String, List<String>> getFinalDelayedMapping(
- Map<String, List<String>> newIdealPreferenceList,
- Map<String, List<String>> newDelayedPreferenceList, Set<String> liveEnabledInstances,
- int minActiveReplica) {
- Map<String, List<String>> finalPreferenceList = new HashMap<>();
- for (String partition : newIdealPreferenceList.keySet()) {
- List<String> idealList = newIdealPreferenceList.get(partition);
- List<String> delayedIdealList = newDelayedPreferenceList.get(partition);
-
- List<String> liveList = new ArrayList<>();
- for (String ins : delayedIdealList) {
- if (liveEnabledInstances.contains(ins)) {
- liveList.add(ins);
- }
- }
-
- if (liveList.size() >= minActiveReplica) {
- finalPreferenceList.put(partition, delayedIdealList);
- } else {
- List<String> candidates = new ArrayList<>(idealList);
- candidates.removeAll(delayedIdealList);
- for (String liveIns : candidates) {
- liveList.add(liveIns);
- if (liveList.size() >= minActiveReplica) {
- break;
- }
- }
- finalPreferenceList.put(partition, liveList);
- }
- }
- return finalPreferenceList;
- }
-
- /**
- * Get the minimum active replica count threshold that allows delayed rebalance.
- *
- * @param idealState the resource Ideal State
- * @param replicaCount the expected active replica count.
- * @return the expected minimum active replica count that is required
- */
- public static int getMinActiveReplica(IdealState idealState, int replicaCount) {
- int minActiveReplicas = idealState.getMinActiveReplicas();
- if (minActiveReplicas < 0) {
- minActiveReplicas = replicaCount;
- }
- return minActiveReplicas;
- }
-
- /**
- * Set a rebalance scheduler for the closest future rebalance time.
- */
- public static void setRebalanceScheduler(String resourceName, boolean isDelayedRebalanceEnabled,
- Set<String> offlineOrDisabledInstances, Map<String, Long> instanceOfflineTimeMap,
- Set<String> liveNodes, Map<String, InstanceConfig> instanceConfigMap, long delay,
- ClusterConfig clusterConfig, HelixManager manager) {
- if (!isDelayedRebalanceEnabled) {
- REBALANCE_SCHEDULER.removeScheduledRebalance(resourceName);
- return;
- }
-
- long currentTime = System.currentTimeMillis();
- long nextRebalanceTime = Long.MAX_VALUE;
- // calculate the closest future rebalance time
- for (String ins : offlineOrDisabledInstances) {
- long inactiveTime = getInactiveTime(ins, liveNodes, instanceOfflineTimeMap.get(ins), delay,
- instanceConfigMap.get(ins), clusterConfig);
- if (inactiveTime != -1 && inactiveTime > currentTime && inactiveTime < nextRebalanceTime) {
- nextRebalanceTime = inactiveTime;
- }
- }
-
- if (nextRebalanceTime == Long.MAX_VALUE) {
- long startTime = REBALANCE_SCHEDULER.removeScheduledRebalance(resourceName);
- if (LOG.isDebugEnabled()) {
- LOG.debug(String
- .format("Remove exist rebalance timer for resource %s at %d\n", resourceName,
- startTime));
- }
- } else {
- long currentScheduledTime = REBALANCE_SCHEDULER.getRebalanceTime(resourceName);
- if (currentScheduledTime < 0 || currentScheduledTime > nextRebalanceTime) {
- REBALANCE_SCHEDULER.scheduleRebalance(manager, resourceName, nextRebalanceTime);
- if (LOG.isDebugEnabled()) {
- LOG.debug(String
- .format("Set next rebalance time for resource %s at time %d\n", resourceName,
- nextRebalanceTime));
- }
- }
- }
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/ResourceUsageCalculator.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/ResourceUsageCalculator.java
index e7a1b94..c2d472a 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/ResourceUsageCalculator.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/ResourceUsageCalculator.java
@@ -1,31 +1,11 @@
package org.apache.helix.controller.rebalancer.util;
-/*
- * 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.
- */
-
import java.util.HashMap;
import java.util.Map;
import org.apache.helix.api.rebalancer.constraint.dataprovider.PartitionWeightProvider;
import org.apache.helix.controller.common.ResourcesStateMap;
import org.apache.helix.model.Partition;
-import org.apache.helix.model.ResourceAssignment;
public class ResourceUsageCalculator {
/**
@@ -53,176 +33,4 @@ public class ResourceUsageCalculator {
}
return newParticipantUsage;
}
-
- /**
- * Measure baseline divergence between baseline assignment and best possible assignment at
- * replica level. Example as below:
- * baseline =
- * {
- * resource1={
- * partition1={
- * instance1=master,
- * instance2=slave
- * },
- * partition2={
- * instance2=slave
- * }
- * }
- * }
- * bestPossible =
- * {
- * resource1={
- * partition1={
- * instance1=master, <--- matched
- * instance3=slave <--- doesn't match
- * },
- * partition2={
- * instance3=master <--- doesn't match
- * }
- * }
- * }
- * baseline divergence = (doesn't match: 2) / (total(matched + doesn't match): 3) = 2/3 ~= 0.66667
- * If divergence == 1.0, all are different(no match); divergence == 0.0, no difference.
- *
- * @param baseline baseline assignment
- * @param bestPossibleAssignment best possible assignment
- * @return double value range at [0.0, 1.0]
- */
- public static double measureBaselineDivergence(Map<String, ResourceAssignment> baseline,
- Map<String, ResourceAssignment> bestPossibleAssignment) {
- int numMatchedReplicas = 0;
- int numTotalBestPossibleReplicas = 0;
-
- // 1. Check resource assignment names.
- for (Map.Entry<String, ResourceAssignment> resourceEntry : bestPossibleAssignment.entrySet()) {
- String resourceKey = resourceEntry.getKey();
- if (!baseline.containsKey(resourceKey)) {
- continue;
- }
-
- // Resource assignment names are matched.
- // 2. check partitions.
- Map<String, Map<String, String>> bestPossiblePartitions =
- resourceEntry.getValue().getRecord().getMapFields();
- Map<String, Map<String, String>> baselinePartitions =
- baseline.get(resourceKey).getRecord().getMapFields();
-
- for (Map.Entry<String, Map<String, String>> partitionEntry
- : bestPossiblePartitions.entrySet()) {
- String partitionName = partitionEntry.getKey();
- if (!baselinePartitions.containsKey(partitionName)) {
- continue;
- }
-
- // Partition names are matched.
- // 3. Check replicas.
- Map<String, String> bestPossibleReplicas = partitionEntry.getValue();
- Map<String, String> baselineReplicas = baselinePartitions.get(partitionName);
-
- for (Map.Entry<String, String> replicaEntry : bestPossibleReplicas.entrySet()) {
- String replicaName = replicaEntry.getKey();
- if (!baselineReplicas.containsKey(replicaName)) {
- continue;
- }
-
- // Replica names are matched.
- // 4. Check replica values.
- String bestPossibleReplica = replicaEntry.getValue();
- String baselineReplica = baselineReplicas.get(replicaName);
- if (bestPossibleReplica.equals(baselineReplica)) {
- numMatchedReplicas++;
- }
- }
-
- // Count total best possible replicas.
- numTotalBestPossibleReplicas += bestPossibleReplicas.size();
- }
- }
-
- return numTotalBestPossibleReplicas == 0 ? 1.0d
- : (1.0d - (double) numMatchedReplicas / (double) numTotalBestPossibleReplicas);
- }
-
- /**
- * Calculates average partition weight per capacity key for a resource config. Example as below:
- * Input =
- * {
- * "partition1": {
- * "capacity1": 20,
- * "capacity2": 40
- * },
- * "partition2": {
- * "capacity1": 30,
- * "capacity2": 50
- * },
- * "partition3": {
- * "capacity1": 16,
- * "capacity2": 30
- * }
- * }
- *
- * Total weight for key "capacity1" = 20 + 30 + 16 = 66;
- * Total weight for key "capacity2" = 40 + 50 + 30 = 120;
- * Total partitions = 3;
- * Average partition weight for "capacity1" = 66 / 3 = 22;
- * Average partition weight for "capacity2" = 120 / 3 = 40;
- *
- * Output =
- * {
- * "capacity1": 22,
- * "capacity2": 40
- * }
- *
- * @param partitionCapacityMap A map of partition capacity:
- * <PartitionName or DEFAULT_PARTITION_KEY, <Capacity Key, Capacity Number>>
- * @return A map of partition weight: capacity key -> average partition weight
- */
- public static Map<String, Integer> calculateAveragePartitionWeight(
- Map<String, Map<String, Integer>> partitionCapacityMap) {
- // capacity key -> [number of partitions, total weight per capacity key]
- Map<String, PartitionWeightCounterEntry> countPartitionWeightMap = new HashMap<>();
-
- // Aggregates partition weight for each capacity key.
- partitionCapacityMap.values().forEach(partitionCapacityEntry ->
- partitionCapacityEntry.forEach((capacityKey, weight) -> countPartitionWeightMap
- .computeIfAbsent(capacityKey, counterEntry -> new PartitionWeightCounterEntry())
- .increase(1, weight)));
-
- // capacity key -> average partition weight
- Map<String, Integer> averagePartitionWeightMap = new HashMap<>();
-
- // Calculate average partition weight for each capacity key.
- // Per capacity key level:
- // average partition weight = (total partition weight) / (number of partitions)
- for (Map.Entry<String, PartitionWeightCounterEntry> entry
- : countPartitionWeightMap.entrySet()) {
- String capacityKey = entry.getKey();
- PartitionWeightCounterEntry weightEntry = entry.getValue();
- int averageWeight = (int) (weightEntry.getWeight() / weightEntry.getPartitions());
- averagePartitionWeightMap.put(capacityKey, averageWeight);
- }
-
- return averagePartitionWeightMap;
- }
-
- /*
- * Represents total number of partitions and total partition weight for a capacity key.
- */
- private static class PartitionWeightCounterEntry {
- private int partitions;
- private long weight;
-
- private int getPartitions() {
- return partitions;
- }
-
- private long getWeight() {
- return weight;
- }
-
- private void increase(int partitions, int weight) {
- this.partitions += partitions;
- this.weight += weight;
- }
- }
}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/WagedValidationUtil.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/WagedValidationUtil.java
deleted file mode 100644
index e9f86e7..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/util/WagedValidationUtil.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package org.apache.helix.controller.rebalancer.util;
-
-/*
- * 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.
- */
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.helix.HelixException;
-import org.apache.helix.model.ClusterConfig;
-import org.apache.helix.model.InstanceConfig;
-import org.apache.helix.model.ResourceConfig;
-
-
-/**
- * A util class that contains validation-related static methods for WAGED rebalancer.
- */
-public class WagedValidationUtil {
- /**
- * Validates and returns instance capacities. The validation logic ensures that all required capacity keys (in ClusterConfig) are present in InstanceConfig.
- * @param clusterConfig
- * @param instanceConfig
- * @return
- */
- public static Map<String, Integer> validateAndGetInstanceCapacity(ClusterConfig clusterConfig,
- InstanceConfig instanceConfig) {
- // Fetch the capacity of instance from 2 possible sources according to the following priority.
- // 1. The instance capacity that is configured in the instance config.
- // 2. If the default instance capacity that is configured in the cluster config contains more capacity keys, fill the capacity map with those additional values.
- Map<String, Integer> instanceCapacity =
- new HashMap<>(clusterConfig.getDefaultInstanceCapacityMap());
- instanceCapacity.putAll(instanceConfig.getInstanceCapacityMap());
-
- List<String> requiredCapacityKeys = clusterConfig.getInstanceCapacityKeys();
- // All the required keys must exist in the instance config.
- if (!instanceCapacity.keySet().containsAll(requiredCapacityKeys)) {
- throw new HelixException(String.format(
- "The required capacity keys: %s are not fully configured in the instance: %s, capacity map: %s.",
- requiredCapacityKeys.toString(), instanceConfig.getInstanceName(),
- instanceCapacity.toString()));
- }
- return instanceCapacity;
- }
-
- /**
- * Validates and returns partition capacities. The validation logic ensures that all required capacity keys (from ClusterConfig) are present in the ResourceConfig for the partition.
- * @param partitionName
- * @param resourceConfig
- * @param clusterConfig
- * @return
- */
- public static Map<String, Integer> validateAndGetPartitionCapacity(String partitionName,
- ResourceConfig resourceConfig, Map<String, Map<String, Integer>> capacityMap,
- ClusterConfig clusterConfig) {
- // Fetch the capacity of partition from 3 possible sources according to the following priority.
- // 1. The partition capacity that is explicitly configured in the resource config.
- // 2. Or, the default partition capacity that is configured under partition name DEFAULT_PARTITION_KEY in the resource config.
- // 3. If the default partition capacity that is configured in the cluster config contains more capacity keys, fill the capacity map with those additional values.
- Map<String, Integer> partitionCapacity =
- new HashMap<>(clusterConfig.getDefaultPartitionWeightMap());
- partitionCapacity.putAll(capacityMap.getOrDefault(partitionName,
- capacityMap.getOrDefault(ResourceConfig.DEFAULT_PARTITION_KEY, new HashMap<>())));
-
- List<String> requiredCapacityKeys = clusterConfig.getInstanceCapacityKeys();
- // If any required capacity key is not configured in the resource config, fail the model creating.
- if (!partitionCapacity.keySet().containsAll(requiredCapacityKeys)) {
- throw new HelixException(String.format(
- "The required capacity keys: %s are not fully configured in the resource: %s, partition: %s, weight map: %s.",
- requiredCapacityKeys.toString(), resourceConfig.getResourceName(), partitionName,
- partitionCapacity.toString()));
- }
- return partitionCapacity;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/AssignmentMetadataStore.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/AssignmentMetadataStore.java
deleted file mode 100644
index afd0187..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/AssignmentMetadataStore.java
+++ /dev/null
@@ -1,213 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged;
-
-/*
- * 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.
- */
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
-import org.apache.helix.BucketDataAccessor;
-import org.apache.helix.HelixException;
-import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.ZNRecordJacksonSerializer;
-import org.apache.helix.manager.zk.ZkBucketDataAccessor;
-import org.apache.helix.model.ResourceAssignment;
-
-
-/**
- * A placeholder before we have the real assignment metadata store.
- */
-public class AssignmentMetadataStore {
- private static final String ASSIGNMENT_METADATA_KEY = "ASSIGNMENT_METADATA";
- private static final String BASELINE_TEMPLATE = "/%s/%s/BASELINE";
- private static final String BEST_POSSIBLE_TEMPLATE = "/%s/%s/BEST_POSSIBLE";
- private static final String BASELINE_KEY = "BASELINE";
- private static final String BEST_POSSIBLE_KEY = "BEST_POSSIBLE";
- private static final ZkSerializer SERIALIZER = new ZNRecordJacksonSerializer();
-
- private BucketDataAccessor _dataAccessor;
- private String _baselinePath;
- private String _bestPossiblePath;
- protected Map<String, ResourceAssignment> _globalBaseline;
- protected Map<String, ResourceAssignment> _bestPossibleAssignment;
-
- AssignmentMetadataStore(String metadataStoreAddrs, String clusterName) {
- this(new ZkBucketDataAccessor(metadataStoreAddrs), clusterName);
- }
-
- protected AssignmentMetadataStore(BucketDataAccessor bucketDataAccessor, String clusterName) {
- _dataAccessor = bucketDataAccessor;
- _baselinePath = String.format(BASELINE_TEMPLATE, clusterName, ASSIGNMENT_METADATA_KEY);
- _bestPossiblePath = String.format(BEST_POSSIBLE_TEMPLATE, clusterName, ASSIGNMENT_METADATA_KEY);
- }
-
- public synchronized Map<String, ResourceAssignment> getBaseline() {
- // Return the in-memory baseline. If null, read from ZK. This is to minimize reads from ZK
- if (_globalBaseline == null) {
- try {
- HelixProperty baseline =
- _dataAccessor.compressedBucketRead(_baselinePath, HelixProperty.class);
- _globalBaseline = splitAssignments(baseline);
- } catch (ZkNoNodeException ex) {
- // Metadata does not exist, so return an empty map
- _globalBaseline = Collections.emptyMap();
- }
- }
- return _globalBaseline;
- }
-
- public synchronized Map<String, ResourceAssignment> getBestPossibleAssignment() {
- // Return the in-memory baseline. If null, read from ZK. This is to minimize reads from ZK
- if (_bestPossibleAssignment == null) {
- try {
- HelixProperty baseline =
- _dataAccessor.compressedBucketRead(_bestPossiblePath, HelixProperty.class);
- _bestPossibleAssignment = splitAssignments(baseline);
- } catch (ZkNoNodeException ex) {
- // Metadata does not exist, so return an empty map
- _bestPossibleAssignment = Collections.emptyMap();
- }
- }
- return _bestPossibleAssignment;
- }
-
- /**
- * @return true if a new baseline was persisted.
- * @throws HelixException if the method failed to persist the baseline.
- */
- // TODO: Enhance the return value so it is more intuitive to understand when the persist fails and
- // TODO: when it is skipped.
- public synchronized boolean persistBaseline(Map<String, ResourceAssignment> globalBaseline) {
- // TODO: Make the write async?
- // If baseline hasn't changed, skip writing to metadata store
- if (compareAssignments(_globalBaseline, globalBaseline)) {
- return false;
- }
- // Persist to ZK
- HelixProperty combinedAssignments = combineAssignments(BASELINE_KEY, globalBaseline);
- try {
- _dataAccessor.compressedBucketWrite(_baselinePath, combinedAssignments);
- } catch (IOException e) {
- // TODO: Improve failure handling
- throw new HelixException("Failed to persist baseline!", e);
- }
-
- // Update the in-memory reference
- _globalBaseline = globalBaseline;
- return true;
- }
-
- /**
- * @return true if a new best possible assignment was persisted.
- * @throws HelixException if the method failed to persist the baseline.
- */
- // TODO: Enhance the return value so it is more intuitive to understand when the persist fails and
- // TODO: when it is skipped.
- public synchronized boolean persistBestPossibleAssignment(
- Map<String, ResourceAssignment> bestPossibleAssignment) {
- // TODO: Make the write async?
- // If bestPossibleAssignment hasn't changed, skip writing to metadata store
- if (compareAssignments(_bestPossibleAssignment, bestPossibleAssignment)) {
- return false;
- }
- // Persist to ZK
- HelixProperty combinedAssignments =
- combineAssignments(BEST_POSSIBLE_KEY, bestPossibleAssignment);
- try {
- _dataAccessor.compressedBucketWrite(_bestPossiblePath, combinedAssignments);
- } catch (IOException e) {
- // TODO: Improve failure handling
- throw new HelixException("Failed to persist BestPossibleAssignment!", e);
- }
-
- // Update the in-memory reference
- _bestPossibleAssignment = bestPossibleAssignment;
- return true;
- }
-
- protected synchronized void reset() {
- if (_bestPossibleAssignment != null) {
- _bestPossibleAssignment.clear();
- _bestPossibleAssignment = null;
- }
- if (_globalBaseline != null) {
- _globalBaseline.clear();
- _globalBaseline = null;
- }
- }
-
- protected void finalize() {
- // To ensure all resources are released.
- close();
- }
-
- // Close to release all the resources.
- public void close() {
- _dataAccessor.disconnect();
- }
-
- /**
- * Produces one HelixProperty that contains all assignment data.
- * @param name
- * @param assignmentMap
- * @return
- */
- private HelixProperty combineAssignments(String name,
- Map<String, ResourceAssignment> assignmentMap) {
- HelixProperty property = new HelixProperty(name);
- // Add each resource's assignment as a simple field in one ZNRecord
- // Node that don't use Arrays.toString() for the record converting. The deserialize will fail.
- assignmentMap.forEach((resource, assignment) -> property.getRecord()
- .setSimpleField(resource, new String(SERIALIZER.serialize(assignment.getRecord()))));
- return property;
- }
-
- /**
- * Returns a Map of (ResourceName, ResourceAssignment) pairs.
- * @param property
- * @return
- */
- private Map<String, ResourceAssignment> splitAssignments(HelixProperty property) {
- Map<String, ResourceAssignment> assignmentMap = new HashMap<>();
- // Convert each resource's assignment String into a ResourceAssignment object and put it in a
- // map
- property.getRecord().getSimpleFields().forEach((resource, assignmentStr) -> assignmentMap
- .put(resource,
- new ResourceAssignment((ZNRecord) SERIALIZER.deserialize(assignmentStr.getBytes()))));
- return assignmentMap;
- }
-
- /**
- * Returns whether two assignments are same.
- * @param oldAssignment
- * @param newAssignment
- * @return true if they are the same. False otherwise or oldAssignment is null
- */
- protected boolean compareAssignments(Map<String, ResourceAssignment> oldAssignment,
- Map<String, ResourceAssignment> newAssignment) {
- // If oldAssignment is null, that means that we haven't read from/written to
- // the metadata store yet. In that case, we return false so that we write to metadata store.
- return oldAssignment != null && oldAssignment.equals(newAssignment);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/RebalanceAlgorithm.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/RebalanceAlgorithm.java
deleted file mode 100644
index 1374162..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/RebalanceAlgorithm.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged;
-
-/*
- * 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.
- */
-
-import org.apache.helix.HelixRebalanceException;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterModel;
-import org.apache.helix.controller.rebalancer.waged.model.OptimalAssignment;
-
-/**
- * A generic interface to generate the optimal assignment given the runtime cluster environment.
- *
- * <pre>
- * @see <a href="https://github.com/apache/helix/wiki/
- * Design-Proposal---Weight-Aware-Globally-Even-Distribute-Rebalancer
- * #rebalance-algorithm-adapter">Rebalance Algorithm</a>
- * </pre>
- */
-public interface RebalanceAlgorithm {
-
- /**
- * Rebalance the Helix resource partitions based on the input cluster model.
- * @param clusterModel The run time cluster model that contains all necessary information
- * @return An instance of {@link OptimalAssignment}
- */
- OptimalAssignment calculate(ClusterModel clusterModel) throws HelixRebalanceException;
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/WagedRebalancer.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/WagedRebalancer.java
deleted file mode 100644
index 8a21bbb..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/WagedRebalancer.java
+++ /dev/null
@@ -1,787 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged;
-
-/*
- * 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.
- */
-
-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.Optional;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.stream.Collectors;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import org.apache.helix.HelixConstants;
-import org.apache.helix.HelixManager;
-import org.apache.helix.HelixRebalanceException;
-import org.apache.helix.controller.changedetector.ResourceChangeDetector;
-import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
-import org.apache.helix.controller.rebalancer.DelayedAutoRebalancer;
-import org.apache.helix.controller.rebalancer.StatefulRebalancer;
-import org.apache.helix.controller.rebalancer.internal.MappingCalculator;
-import org.apache.helix.controller.rebalancer.util.DelayedRebalanceUtil;
-import org.apache.helix.controller.rebalancer.waged.constraints.ConstraintBasedAlgorithmFactory;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterModel;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterModelProvider;
-import org.apache.helix.controller.rebalancer.waged.model.OptimalAssignment;
-import org.apache.helix.controller.stages.CurrentStateOutput;
-import org.apache.helix.model.ClusterConfig;
-import org.apache.helix.model.IdealState;
-import org.apache.helix.model.Partition;
-import org.apache.helix.model.Resource;
-import org.apache.helix.model.ResourceAssignment;
-import org.apache.helix.model.ResourceConfig;
-import org.apache.helix.monitoring.metrics.MetricCollector;
-import org.apache.helix.monitoring.metrics.WagedRebalancerMetricCollector;
-import org.apache.helix.monitoring.metrics.implementation.BaselineDivergenceGauge;
-import org.apache.helix.monitoring.metrics.model.CountMetric;
-import org.apache.helix.monitoring.metrics.model.LatencyMetric;
-import org.apache.helix.util.RebalanceUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Weight-Aware Globally-Even Distribute Rebalancer.
- * @see <a
- * href="https://github.com/apache/helix/wiki/Design-Proposal---Weight-Aware-Globally-Even-Distribute-Rebalancer">
- * Design Document
- * </a>
- */
-public class WagedRebalancer implements StatefulRebalancer<ResourceControllerDataProvider> {
- private static final Logger LOG = LoggerFactory.getLogger(WagedRebalancer.class);
-
- // When any of the following change happens, the rebalancer needs to do a global rebalance which
- // contains 1. baseline recalculate, 2. partial rebalance that is based on the new baseline.
- private static final Set<HelixConstants.ChangeType> GLOBAL_REBALANCE_REQUIRED_CHANGE_TYPES =
- ImmutableSet
- .of(HelixConstants.ChangeType.RESOURCE_CONFIG, HelixConstants.ChangeType.IDEAL_STATE,
- HelixConstants.ChangeType.CLUSTER_CONFIG, HelixConstants.ChangeType.INSTANCE_CONFIG);
- // To identify if the preference has been configured or not.
- private static final Map<ClusterConfig.GlobalRebalancePreferenceKey, Integer>
- NOT_CONFIGURED_PREFERENCE = ImmutableMap
- .of(ClusterConfig.GlobalRebalancePreferenceKey.EVENNESS, -1,
- ClusterConfig.GlobalRebalancePreferenceKey.LESS_MOVEMENT, -1);
- // The default algorithm to use when there is no preference configured.
- private static final RebalanceAlgorithm DEFAULT_REBALANCE_ALGORITHM =
- ConstraintBasedAlgorithmFactory
- .getInstance(ClusterConfig.DEFAULT_GLOBAL_REBALANCE_PREFERENCE);
-
- // To calculate the baseline asynchronously
- private final ExecutorService _baselineCalculateExecutor;
- private final ResourceChangeDetector _changeDetector;
- private final HelixManager _manager;
- private final MappingCalculator<ResourceControllerDataProvider> _mappingCalculator;
- private final AssignmentMetadataStore _assignmentMetadataStore;
-
- private final MetricCollector _metricCollector;
- private final CountMetric _rebalanceFailureCount;
- private final CountMetric _baselineCalcCounter;
- private final LatencyMetric _baselineCalcLatency;
- private final LatencyMetric _writeLatency;
- private final CountMetric _partialRebalanceCounter;
- private final LatencyMetric _partialRebalanceLatency;
- private final LatencyMetric _stateReadLatency;
- private final BaselineDivergenceGauge _baselineDivergenceGauge;
-
- private boolean _asyncGlobalRebalanceEnabled;
-
- // Note, the rebalance algorithm field is mutable so it should not be directly referred except for
- // the public method computeNewIdealStates.
- private RebalanceAlgorithm _rebalanceAlgorithm;
- private Map<ClusterConfig.GlobalRebalancePreferenceKey, Integer> _preference =
- NOT_CONFIGURED_PREFERENCE;
-
- private static AssignmentMetadataStore constructAssignmentStore(String metadataStoreAddrs,
- String clusterName) {
- if (metadataStoreAddrs != null && clusterName != null) {
- return new AssignmentMetadataStore(metadataStoreAddrs, clusterName);
- }
- return null;
- }
-
- public WagedRebalancer(HelixManager helixManager) {
- this(helixManager == null ? null
- : constructAssignmentStore(helixManager.getMetadataStoreConnectionString(),
- helixManager.getClusterName()),
- DEFAULT_REBALANCE_ALGORITHM,
- // Use DelayedAutoRebalancer as the mapping calculator for the final assignment output.
- // Mapping calculator will translate the best possible assignment into the applicable state
- // mapping based on the current states.
- // TODO abstract and separate the main mapping calculator logic from DelayedAutoRebalancer
- new DelayedAutoRebalancer(),
- // Helix Manager is required for the rebalancer scheduler
- helixManager,
- // If HelixManager is null, we just pass in a non-functioning WagedRebalancerMetricCollector
- // that will not be registered to MBean.
- // This is to handle two cases: 1. HelixManager is null for non-testing cases. In this case,
- // WagedRebalancer will not read/write to metadata store and just use CurrentState-based
- // rebalancing. 2. Tests that require instrumenting the rebalancer for verifying whether the
- // cluster has converged.
- helixManager == null ? new WagedRebalancerMetricCollector()
- : new WagedRebalancerMetricCollector(helixManager.getClusterName()),
- ClusterConfig.DEFAULT_GLOBAL_REBALANCE_ASYNC_MODE_ENABLED);
- _preference = ImmutableMap.copyOf(ClusterConfig.DEFAULT_GLOBAL_REBALANCE_PREFERENCE);
- }
-
- /**
- * This constructor will use null for HelixManager. With null HelixManager, the rebalancer will
- * not schedule for a future delayed rebalance.
- * @param assignmentMetadataStore
- * @param algorithm
- * @param metricCollectorOptional
- */
- protected WagedRebalancer(AssignmentMetadataStore assignmentMetadataStore,
- RebalanceAlgorithm algorithm, Optional<MetricCollector> metricCollectorOptional) {
- this(assignmentMetadataStore, algorithm, new DelayedAutoRebalancer(), null,
- // If metricCollector is not provided, instantiate a version that does not register metrics
- // in order to allow rebalancer to proceed
- metricCollectorOptional.orElse(new WagedRebalancerMetricCollector()),
- false);
- }
-
- private WagedRebalancer(AssignmentMetadataStore assignmentMetadataStore,
- RebalanceAlgorithm algorithm, MappingCalculator mappingCalculator, HelixManager manager,
- MetricCollector metricCollector, boolean isAsyncGlobalRebalanceEnabled) {
- if (assignmentMetadataStore == null) {
- LOG.warn("Assignment Metadata Store is not configured properly."
- + " The rebalancer will not access the assignment store during the rebalance.");
- }
- _assignmentMetadataStore = assignmentMetadataStore;
- _rebalanceAlgorithm = algorithm;
- _mappingCalculator = mappingCalculator;
- if (manager == null) {
- LOG.warn("HelixManager is not provided. The rebalancer is not going to schedule for a future "
- + "rebalance even when delayed rebalance is enabled.");
- }
- _manager = manager;
-
- _metricCollector = metricCollector;
- _rebalanceFailureCount = _metricCollector.getMetric(
- WagedRebalancerMetricCollector.WagedRebalancerMetricNames.RebalanceFailureCounter.name(),
- CountMetric.class);
- _baselineCalcCounter = _metricCollector.getMetric(
- WagedRebalancerMetricCollector.WagedRebalancerMetricNames.GlobalBaselineCalcCounter.name(),
- CountMetric.class);
- _baselineCalcLatency = _metricCollector.getMetric(
- WagedRebalancerMetricCollector.WagedRebalancerMetricNames.GlobalBaselineCalcLatencyGauge
- .name(),
- LatencyMetric.class);
- _partialRebalanceCounter = _metricCollector.getMetric(
- WagedRebalancerMetricCollector.WagedRebalancerMetricNames.PartialRebalanceCounter.name(),
- CountMetric.class);
- _partialRebalanceLatency = _metricCollector.getMetric(
- WagedRebalancerMetricCollector.WagedRebalancerMetricNames.PartialRebalanceLatencyGauge
- .name(),
- LatencyMetric.class);
- _writeLatency = _metricCollector.getMetric(
- WagedRebalancerMetricCollector.WagedRebalancerMetricNames.StateWriteLatencyGauge.name(),
- LatencyMetric.class);
- _stateReadLatency = _metricCollector.getMetric(
- WagedRebalancerMetricCollector.WagedRebalancerMetricNames.StateReadLatencyGauge.name(),
- LatencyMetric.class);
- _baselineDivergenceGauge = _metricCollector.getMetric(
- WagedRebalancerMetricCollector.WagedRebalancerMetricNames.BaselineDivergenceGauge.name(),
- BaselineDivergenceGauge.class);
-
- _changeDetector = new ResourceChangeDetector(true);
-
- _baselineCalculateExecutor = Executors.newSingleThreadExecutor();
- _asyncGlobalRebalanceEnabled = isAsyncGlobalRebalanceEnabled;
- }
-
- // Update the global rebalance mode to be asynchronous or synchronous
- public void setGlobalRebalanceAsyncMode(boolean isAsyncGlobalRebalanceEnabled) {
- _asyncGlobalRebalanceEnabled = isAsyncGlobalRebalanceEnabled;
- }
-
- // Update the rebalancer preference if the new options are different from the current preference.
- public synchronized void updateRebalancePreference(
- Map<ClusterConfig.GlobalRebalancePreferenceKey, Integer> newPreference) {
- // 1. if the preference was not configured during constructing, no need to update.
- // 2. if the preference equals to the new preference, no need to update.
- if (!_preference.equals(NOT_CONFIGURED_PREFERENCE) && !_preference.equals(newPreference)) {
- _rebalanceAlgorithm = ConstraintBasedAlgorithmFactory.getInstance(newPreference);
- _preference = ImmutableMap.copyOf(newPreference);
- }
- }
-
- @Override
- public void reset() {
- if (_assignmentMetadataStore != null) {
- _assignmentMetadataStore.reset();
- }
- _changeDetector.resetSnapshots();
- }
-
- // TODO the rebalancer should reject any other computing request after being closed.
- @Override
- public void close() {
- if (_baselineCalculateExecutor != null) {
- _baselineCalculateExecutor.shutdownNow();
- }
- if (_assignmentMetadataStore != null) {
- _assignmentMetadataStore.close();
- }
- _metricCollector.unregister();
- }
-
- @Override
- public Map<String, IdealState> computeNewIdealStates(ResourceControllerDataProvider clusterData,
- Map<String, Resource> resourceMap, final CurrentStateOutput currentStateOutput)
- throws HelixRebalanceException {
- if (resourceMap.isEmpty()) {
- LOG.warn("There is no resource to be rebalanced by {}", this.getClass().getSimpleName());
- return Collections.emptyMap();
- }
-
- LOG.info("Start computing new ideal states for resources: {}", resourceMap.keySet().toString());
- validateInput(clusterData, resourceMap);
-
- Map<String, IdealState> newIdealStates;
- try {
- // Calculate the target assignment based on the current cluster status.
- newIdealStates = computeBestPossibleStates(clusterData, resourceMap, currentStateOutput,
- _rebalanceAlgorithm);
- } catch (HelixRebalanceException ex) {
- LOG.error("Failed to calculate the new assignments.", ex);
- // Record the failure in metrics.
- _rebalanceFailureCount.increment(1L);
-
- HelixRebalanceException.Type failureType = ex.getFailureType();
- if (failureType.equals(HelixRebalanceException.Type.INVALID_REBALANCER_STATUS) || failureType
- .equals(HelixRebalanceException.Type.UNKNOWN_FAILURE)) {
- // If the failure is unknown or because of assignment store access failure, throw the
- // rebalance exception.
- throw ex;
- } else { // return the previously calculated assignment.
- LOG.warn(
- "Returning the last known-good best possible assignment from metadata store due to "
- + "rebalance failure of type: {}", failureType);
- // Note that don't return an assignment based on the current state if there is no previously
- // calculated result in this fallback logic.
- Map<String, ResourceAssignment> assignmentRecord =
- getBestPossibleAssignment(_assignmentMetadataStore, new CurrentStateOutput(),
- resourceMap.keySet());
- newIdealStates = convertResourceAssignment(clusterData, assignmentRecord);
- }
- }
-
- // Construct the new best possible states according to the current state and target assignment.
- // Note that the new ideal state might be an intermediate state between the current state and
- // the target assignment.
- newIdealStates.values().parallelStream().forEach(idealState -> {
- String resourceName = idealState.getResourceName();
- // Adjust the states according to the current state.
- ResourceAssignment finalAssignment = _mappingCalculator
- .computeBestPossiblePartitionState(clusterData, idealState, resourceMap.get(resourceName),
- currentStateOutput);
-
- // Clean up the state mapping fields. Use the final assignment that is calculated by the
- // mapping calculator to replace them.
- idealState.getRecord().getMapFields().clear();
- for (Partition partition : finalAssignment.getMappedPartitions()) {
- Map<String, String> newStateMap = finalAssignment.getReplicaMap(partition);
- // if the final states cannot be generated, override the best possible state with empty map.
- idealState.setInstanceStateMap(partition.getPartitionName(),
- newStateMap == null ? Collections.emptyMap() : newStateMap);
- }
- });
- LOG.info("Finish computing new ideal states for resources: {}",
- resourceMap.keySet().toString());
- return newIdealStates;
- }
-
- // Coordinate global rebalance and partial rebalance according to the cluster changes.
- private Map<String, IdealState> computeBestPossibleStates(
- ResourceControllerDataProvider clusterData, Map<String, Resource> resourceMap,
- final CurrentStateOutput currentStateOutput, RebalanceAlgorithm algorithm)
- throws HelixRebalanceException {
- Set<String> activeNodes = DelayedRebalanceUtil
- .getActiveNodes(clusterData.getAllInstances(), clusterData.getEnabledLiveInstances(),
- clusterData.getInstanceOfflineTimeMap(), clusterData.getLiveInstances().keySet(),
- clusterData.getInstanceConfigMap(), clusterData.getClusterConfig());
-
- // Schedule (or unschedule) delayed rebalance according to the delayed rebalance config.
- delayedRebalanceSchedule(clusterData, activeNodes, resourceMap.keySet());
-
- Map<String, IdealState> newIdealStates = convertResourceAssignment(clusterData,
- computeBestPossibleAssignment(clusterData, resourceMap, activeNodes, currentStateOutput,
- algorithm));
-
- // The additional rebalance overwrite is required since the calculated mapping may contain
- // some delayed rebalanced assignments.
- if (!activeNodes.equals(clusterData.getEnabledLiveInstances())) {
- applyRebalanceOverwrite(newIdealStates, clusterData, resourceMap,
- getBaselineAssignment(_assignmentMetadataStore, currentStateOutput, resourceMap.keySet()),
- algorithm);
- }
- // Replace the assignment if user-defined preference list is configured.
- // Note the user-defined list is intentionally applied to the final mapping after calculation.
- // This is to avoid persisting it into the assignment store, which impacts the long term
- // assignment evenness and partition movements.
- newIdealStates.entrySet().stream().forEach(idealStateEntry -> applyUserDefinedPreferenceList(
- clusterData.getResourceConfig(idealStateEntry.getKey()), idealStateEntry.getValue()));
-
- return newIdealStates;
- }
-
- // Coordinate global rebalance and partial rebalance according to the cluster changes.
- protected Map<String, ResourceAssignment> computeBestPossibleAssignment(
- ResourceControllerDataProvider clusterData, Map<String, Resource> resourceMap,
- Set<String> activeNodes, final CurrentStateOutput currentStateOutput,
- RebalanceAlgorithm algorithm)
- throws HelixRebalanceException {
- // Perform global rebalance for a new baseline assignment
- globalRebalance(clusterData, resourceMap, currentStateOutput, algorithm);
- // Perform partial rebalance for a new best possible assignment
- Map<String, ResourceAssignment> newAssignment =
- partialRebalance(clusterData, resourceMap, activeNodes, currentStateOutput, algorithm);
- return newAssignment;
- }
-
- /**
- * Convert the resource assignment map into an IdealState map.
- */
- private Map<String, IdealState> convertResourceAssignment(
- ResourceControllerDataProvider clusterData, Map<String, ResourceAssignment> assignments)
- throws HelixRebalanceException {
- // Convert the assignments into IdealState for the following state mapping calculation.
- Map<String, IdealState> finalIdealStateMap = new HashMap<>();
- for (String resourceName : assignments.keySet()) {
- try {
- IdealState currentIdealState = clusterData.getIdealState(resourceName);
- Map<String, Integer> statePriorityMap =
- clusterData.getStateModelDef(currentIdealState.getStateModelDefRef())
- .getStatePriorityMap();
- // Create a new IdealState instance which contains the new calculated assignment in the
- // preference list.
- IdealState newIdealState = new IdealState(resourceName);
- // Copy the simple fields
- newIdealState.getRecord().setSimpleFields(currentIdealState.getRecord().getSimpleFields());
- // Sort the preference list according to state priority.
- newIdealState.setPreferenceLists(
- getPreferenceLists(assignments.get(resourceName), statePriorityMap));
- // Note the state mapping in the new assignment won't directly propagate to the map fields.
- // The rebalancer will calculate for the final state mapping considering the current states.
- finalIdealStateMap.put(resourceName, newIdealState);
- } catch (Exception ex) {
- throw new HelixRebalanceException(
- "Failed to calculate the new IdealState for resource: " + resourceName,
- HelixRebalanceException.Type.INVALID_CLUSTER_STATUS, ex);
- }
- }
- return finalIdealStateMap;
- }
-
- /**
- * Global rebalance calculates for a new baseline assignment.
- * The new baseline assignment will be persisted and leveraged by the partial rebalance.
- * @param clusterData
- * @param resourceMap
- * @param currentStateOutput
- * @param algorithm
- * @throws HelixRebalanceException
- */
- private void globalRebalance(ResourceControllerDataProvider clusterData,
- Map<String, Resource> resourceMap, final CurrentStateOutput currentStateOutput,
- RebalanceAlgorithm algorithm)
- throws HelixRebalanceException {
- _changeDetector.updateSnapshots(clusterData);
- // Get all the changed items' information. Filter for the items that have content changed.
- final Map<HelixConstants.ChangeType, Set<String>> clusterChanges =
- _changeDetector.getAllChanges();
-
- if (clusterChanges.keySet().stream()
- .anyMatch(GLOBAL_REBALANCE_REQUIRED_CHANGE_TYPES::contains)) {
- // Build the cluster model for rebalance calculation.
- // Note, for a Baseline calculation,
- // 1. Ignore node status (disable/offline).
- // 2. Use the previous Baseline as the only parameter about the previous assignment.
- Map<String, ResourceAssignment> currentBaseline =
- getBaselineAssignment(_assignmentMetadataStore, currentStateOutput, resourceMap.keySet());
- ClusterModel clusterModel;
- try {
- clusterModel = ClusterModelProvider
- .generateClusterModelForBaseline(clusterData, resourceMap,
- clusterData.getAllInstances(), clusterChanges, currentBaseline);
- } catch (Exception ex) {
- throw new HelixRebalanceException("Failed to generate cluster model for global rebalance.",
- HelixRebalanceException.Type.INVALID_CLUSTER_STATUS, ex);
- }
-
- final boolean waitForGlobalRebalance = !_asyncGlobalRebalanceEnabled;
- final String clusterName = clusterData.getClusterName();
- // Calculate the Baseline assignment for global rebalance.
- Future<Boolean> result = _baselineCalculateExecutor.submit(() -> {
- try {
- // Note that we should schedule a new partial rebalance for a future rebalance pipeline if
- // the planned partial rebalance in the current rebalance pipeline won't wait for the new
- // baseline being calculated.
- // So set shouldSchedulePartialRebalance to be !waitForGlobalRebalance
- calculateAndUpdateBaseline(clusterModel, algorithm, !waitForGlobalRebalance, clusterName);
- } catch (HelixRebalanceException e) {
- LOG.error("Failed to calculate baseline assignment!", e);
- return false;
- }
- return true;
- });
- if (waitForGlobalRebalance) {
- try {
- if (!result.get()) {
- throw new HelixRebalanceException("Failed to calculate for the new Baseline.",
- HelixRebalanceException.Type.FAILED_TO_CALCULATE);
- }
- } catch (InterruptedException | ExecutionException e) {
- throw new HelixRebalanceException("Failed to execute new Baseline calculation.",
- HelixRebalanceException.Type.FAILED_TO_CALCULATE, e);
- }
- }
- }
- }
-
- /**
- * Calculate and update the Baseline assignment
- * @param clusterModel
- * @param algorithm
- * @param shouldSchedulePartialRebalance True if the call should trigger a following partial rebalance
- * so the new Baseline could be applied to cluster.
- * @param clusterName
- * @throws HelixRebalanceException
- */
- private void calculateAndUpdateBaseline(ClusterModel clusterModel, RebalanceAlgorithm algorithm,
- boolean shouldSchedulePartialRebalance, String clusterName)
- throws HelixRebalanceException {
- LOG.info("Start calculating the new baseline.");
- _baselineCalcCounter.increment(1L);
- _baselineCalcLatency.startMeasuringLatency();
-
- boolean isBaselineChanged = false;
- Map<String, ResourceAssignment> newBaseline = calculateAssignment(clusterModel, algorithm);
- // Write the new baseline to metadata store
- if (_assignmentMetadataStore != null) {
- try {
- _writeLatency.startMeasuringLatency();
- isBaselineChanged = _assignmentMetadataStore.persistBaseline(newBaseline);
- _writeLatency.endMeasuringLatency();
- } catch (Exception ex) {
- throw new HelixRebalanceException("Failed to persist the new baseline assignment.",
- HelixRebalanceException.Type.INVALID_REBALANCER_STATUS, ex);
- }
- } else {
- LOG.debug("Assignment Metadata Store is null. Skip persisting the baseline assignment.");
- }
- _baselineCalcLatency.endMeasuringLatency();
- LOG.info("Global baseline calculation completed and has been persisted into metadata store.");
-
- if (isBaselineChanged && shouldSchedulePartialRebalance) {
- LOG.info("Schedule a new rebalance after the new baseline calculation has finished.");
- RebalanceUtil.scheduleOnDemandPipeline(clusterName, 0L, false);
- }
- }
-
- private Map<String, ResourceAssignment> partialRebalance(
- ResourceControllerDataProvider clusterData, Map<String, Resource> resourceMap,
- Set<String> activeNodes, final CurrentStateOutput currentStateOutput,
- RebalanceAlgorithm algorithm)
- throws HelixRebalanceException {
- LOG.info("Start calculating the new best possible assignment.");
- _partialRebalanceCounter.increment(1L);
- _partialRebalanceLatency.startMeasuringLatency();
- // TODO: Consider combining the metrics for both baseline/best possible?
- // Read the baseline from metadata store
- Map<String, ResourceAssignment> currentBaseline =
- getBaselineAssignment(_assignmentMetadataStore, currentStateOutput, resourceMap.keySet());
-
- // Read the best possible assignment from metadata store
- Map<String, ResourceAssignment> currentBestPossibleAssignment =
- getBestPossibleAssignment(_assignmentMetadataStore, currentStateOutput,
- resourceMap.keySet());
- ClusterModel clusterModel;
- try {
- clusterModel = ClusterModelProvider
- .generateClusterModelForPartialRebalance(clusterData, resourceMap, activeNodes,
- currentBaseline, currentBestPossibleAssignment);
- } catch (Exception ex) {
- throw new HelixRebalanceException("Failed to generate cluster model for partial rebalance.",
- HelixRebalanceException.Type.INVALID_CLUSTER_STATUS, ex);
- }
- Map<String, ResourceAssignment> newAssignment = calculateAssignment(clusterModel, algorithm);
-
- // Asynchronously report baseline divergence metric before persisting to metadata store,
- // just in case if persisting fails, we still have the metric.
- // To avoid changes of the new assignment and make it safe when being used to measure baseline
- // divergence, use a deep copy of the new assignment.
- Map<String, ResourceAssignment> newAssignmentCopy = new HashMap<>();
- for (Map.Entry<String, ResourceAssignment> entry : newAssignment.entrySet()) {
- newAssignmentCopy.put(entry.getKey(), new ResourceAssignment(entry.getValue().getRecord()));
- }
-
- _baselineDivergenceGauge.asyncMeasureAndUpdateValue(clusterData.getAsyncTasksThreadPool(),
- currentBaseline, newAssignmentCopy);
-
- if (_assignmentMetadataStore != null) {
- try {
- _writeLatency.startMeasuringLatency();
- _assignmentMetadataStore.persistBestPossibleAssignment(newAssignment);
- _writeLatency.endMeasuringLatency();
- } catch (Exception ex) {
- throw new HelixRebalanceException("Failed to persist the new best possible assignment.",
- HelixRebalanceException.Type.INVALID_REBALANCER_STATUS, ex);
- }
- } else {
- LOG.debug("Assignment Metadata Store is null. Skip persisting the baseline assignment.");
- }
- _partialRebalanceLatency.endMeasuringLatency();
- LOG.info("Finish calculating the new best possible assignment.");
- return newAssignment;
- }
-
- /**
- * @param clusterModel the cluster model that contains all the cluster status for the purpose of
- * rebalancing.
- * @return the new optimal assignment for the resources.
- */
- private Map<String, ResourceAssignment> calculateAssignment(ClusterModel clusterModel,
- RebalanceAlgorithm algorithm) throws HelixRebalanceException {
- long startTime = System.currentTimeMillis();
- LOG.info("Start calculating for an assignment with algorithm {}",
- algorithm.getClass().getSimpleName());
- OptimalAssignment optimalAssignment = algorithm.calculate(clusterModel);
- Map<String, ResourceAssignment> newAssignment =
- optimalAssignment.getOptimalResourceAssignment();
- LOG.info("Finish calculating an assignment with algorithm {}. Took: {} ms.",
- algorithm.getClass().getSimpleName(), System.currentTimeMillis() - startTime);
- return newAssignment;
- }
-
- // Generate the preference lists from the state mapping based on state priority.
- private Map<String, List<String>> getPreferenceLists(ResourceAssignment newAssignment,
- Map<String, Integer> statePriorityMap) {
- Map<String, List<String>> preferenceList = new HashMap<>();
- for (Partition partition : newAssignment.getMappedPartitions()) {
- List<String> nodes = new ArrayList<>(newAssignment.getReplicaMap(partition).keySet());
- // To ensure backward compatibility, sort the preference list according to state priority.
- nodes.sort((node1, node2) -> {
- int statePriority1 =
- statePriorityMap.get(newAssignment.getReplicaMap(partition).get(node1));
- int statePriority2 =
- statePriorityMap.get(newAssignment.getReplicaMap(partition).get(node2));
- if (statePriority1 == statePriority2) {
- return node1.compareTo(node2);
- } else {
- return statePriority1 - statePriority2;
- }
- });
- preferenceList.put(partition.getPartitionName(), nodes);
- }
- return preferenceList;
- }
-
- private void validateInput(ResourceControllerDataProvider clusterData,
- Map<String, Resource> resourceMap) throws HelixRebalanceException {
- Set<String> nonCompatibleResources = resourceMap.entrySet().stream().filter(resourceEntry -> {
- IdealState is = clusterData.getIdealState(resourceEntry.getKey());
- return is == null || !is.getRebalanceMode().equals(IdealState.RebalanceMode.FULL_AUTO)
- || !WagedRebalancer.class.getName().equals(is.getRebalancerClassName());
- }).map(Map.Entry::getKey).collect(Collectors.toSet());
- if (!nonCompatibleResources.isEmpty()) {
- throw new HelixRebalanceException(String.format(
- "Input contains invalid resource(s) that cannot be rebalanced by the WAGED rebalancer. %s",
- nonCompatibleResources.toString()), HelixRebalanceException.Type.INVALID_INPUT);
- }
- }
-
- /**
- * @param assignmentMetadataStore
- * @param currentStateOutput
- * @param resources
- * @return The current baseline assignment. If record does not exist in the
- * assignmentMetadataStore, return the current state assignment.
- * @throws HelixRebalanceException
- */
- private Map<String, ResourceAssignment> getBaselineAssignment(
- AssignmentMetadataStore assignmentMetadataStore, CurrentStateOutput currentStateOutput,
- Set<String> resources) throws HelixRebalanceException {
- Map<String, ResourceAssignment> currentBaseline = Collections.emptyMap();
- if (assignmentMetadataStore != null) {
- try {
- _stateReadLatency.startMeasuringLatency();
- currentBaseline = assignmentMetadataStore.getBaseline();
- _stateReadLatency.endMeasuringLatency();
- } catch (Exception ex) {
- throw new HelixRebalanceException(
- "Failed to get the current baseline assignment because of unexpected error.",
- HelixRebalanceException.Type.INVALID_REBALANCER_STATUS, ex);
- }
- }
- if (currentBaseline.isEmpty()) {
- LOG.warn("The current baseline assignment record is empty. Use the current states instead.");
- currentBaseline = currentStateOutput.getAssignment(resources);
- }
- currentBaseline.keySet().retainAll(resources);
- return currentBaseline;
- }
-
- /**
- * @param assignmentMetadataStore
- * @param currentStateOutput
- * @param resources
- * @return The current best possible assignment. If record does not exist in the
- * assignmentMetadataStore, return the current state assignment.
- * @throws HelixRebalanceException
- */
- protected Map<String, ResourceAssignment> getBestPossibleAssignment(
- AssignmentMetadataStore assignmentMetadataStore, CurrentStateOutput currentStateOutput,
- Set<String> resources) throws HelixRebalanceException {
- Map<String, ResourceAssignment> currentBestAssignment = Collections.emptyMap();
- if (assignmentMetadataStore != null) {
- try {
- _stateReadLatency.startMeasuringLatency();
- currentBestAssignment = assignmentMetadataStore.getBestPossibleAssignment();
- _stateReadLatency.endMeasuringLatency();
- } catch (Exception ex) {
- throw new HelixRebalanceException(
- "Failed to get the current best possible assignment because of unexpected error.",
- HelixRebalanceException.Type.INVALID_REBALANCER_STATUS, ex);
- }
- }
- if (currentBestAssignment.isEmpty()) {
- LOG.warn(
- "The current best possible assignment record is empty. Use the current states instead.");
- currentBestAssignment = currentStateOutput.getAssignment(resources);
- }
- currentBestAssignment.keySet().retainAll(resources);
- return currentBestAssignment;
- }
-
- /**
- * Schedule rebalance according to the delayed rebalance logic.
- * @param clusterData the current cluster data cache
- * @param delayedActiveNodes the active nodes set that is calculated with the delay time window
- * @param resourceSet the rebalanced resourceSet
- */
- private void delayedRebalanceSchedule(ResourceControllerDataProvider clusterData,
- Set<String> delayedActiveNodes, Set<String> resourceSet) {
- if (_manager != null) {
- // Schedule for the next delayed rebalance in case no cluster change event happens.
- ClusterConfig clusterConfig = clusterData.getClusterConfig();
- boolean delayedRebalanceEnabled = DelayedRebalanceUtil.isDelayRebalanceEnabled(clusterConfig);
- Set<String> offlineOrDisabledInstances = new HashSet<>(delayedActiveNodes);
- offlineOrDisabledInstances.removeAll(clusterData.getEnabledLiveInstances());
- for (String resource : resourceSet) {
- DelayedRebalanceUtil
- .setRebalanceScheduler(resource, delayedRebalanceEnabled, offlineOrDisabledInstances,
- clusterData.getInstanceOfflineTimeMap(), clusterData.getLiveInstances().keySet(),
- clusterData.getInstanceConfigMap(), clusterConfig.getRebalanceDelayTime(),
- clusterConfig, _manager);
- }
- } else {
- LOG.warn("Skip scheduling a delayed rebalancer since HelixManager is not specified.");
- }
- }
-
- /**
- * Update the rebalanced ideal states according to the real active nodes.
- * Since the rebalancing might be done with the delayed logic, the rebalanced ideal states
- * might include inactive nodes.
- * This overwrite will adjust the final mapping, so as to ensure the result is completely valid.
- * @param idealStateMap the calculated ideal states.
- * @param clusterData the cluster data cache.
- * @param resourceMap the rebalanaced resource map.
- * @param baseline the baseline assignment.
- * @param algorithm the rebalance algorithm.
- */
- private void applyRebalanceOverwrite(Map<String, IdealState> idealStateMap,
- ResourceControllerDataProvider clusterData, Map<String, Resource> resourceMap,
- Map<String, ResourceAssignment> baseline, RebalanceAlgorithm algorithm)
- throws HelixRebalanceException {
- ClusterModel clusterModel;
- try {
- // Note this calculation uses the baseline as the best possible assignment input here.
- // This is for minimizing unnecessary partition movement.
- clusterModel = ClusterModelProvider
- .generateClusterModelFromExistingAssignment(clusterData, resourceMap, baseline);
- } catch (Exception ex) {
- throw new HelixRebalanceException(
- "Failed to generate cluster model for delayed rebalance overwrite.",
- HelixRebalanceException.Type.INVALID_CLUSTER_STATUS, ex);
- }
- Map<String, IdealState> activeIdealStates =
- convertResourceAssignment(clusterData, calculateAssignment(clusterModel, algorithm));
- for (String resourceName : idealStateMap.keySet()) {
- // The new calculated ideal state before overwrite
- IdealState newIdealState = idealStateMap.get(resourceName);
- if (!activeIdealStates.containsKey(resourceName)) {
- throw new HelixRebalanceException(
- "Failed to calculate the complete partition assignment with all active nodes. Cannot find the resource assignment for "
- + resourceName, HelixRebalanceException.Type.FAILED_TO_CALCULATE);
- }
- // The ideal state that is calculated based on the real alive/enabled instances list
- IdealState newActiveIdealState = activeIdealStates.get(resourceName);
- // The current ideal state that exists in the IdealState znode
- IdealState currentIdealState = clusterData.getIdealState(resourceName);
- Set<String> enabledLiveInstances = clusterData.getEnabledLiveInstances();
- int numReplica = currentIdealState.getReplicaCount(enabledLiveInstances.size());
- int minActiveReplica =
- DelayedRebalanceUtil.getMinActiveReplica(currentIdealState, numReplica);
- Map<String, List<String>> finalPreferenceLists = DelayedRebalanceUtil
- .getFinalDelayedMapping(newActiveIdealState.getPreferenceLists(),
- newIdealState.getPreferenceLists(), enabledLiveInstances,
- Math.min(minActiveReplica, numReplica));
-
- newIdealState.setPreferenceLists(finalPreferenceLists);
- }
- }
-
- private void applyUserDefinedPreferenceList(ResourceConfig resourceConfig,
- IdealState idealState) {
- if (resourceConfig != null) {
- Map<String, List<String>> userDefinedPreferenceList = resourceConfig.getPreferenceLists();
- if (!userDefinedPreferenceList.isEmpty()) {
- LOG.info("Using user defined preference list for partitions.");
- for (String partition : userDefinedPreferenceList.keySet()) {
- idealState.setPreferenceList(partition, userDefinedPreferenceList.get(partition));
- }
- }
- }
- }
-
- protected AssignmentMetadataStore getAssignmentMetadataStore() {
- return _assignmentMetadataStore;
- }
-
- protected MetricCollector getMetricCollector() {
- return _metricCollector;
- }
-
- @Override
- protected void finalize()
- throws Throwable {
- super.finalize();
- close();
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ConstraintBasedAlgorithm.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ConstraintBasedAlgorithm.java
deleted file mode 100644
index dcadff6..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ConstraintBasedAlgorithm.java
+++ /dev/null
@@ -1,228 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
-import com.google.common.collect.Maps;
-import org.apache.helix.HelixRebalanceException;
-import org.apache.helix.controller.rebalancer.waged.RebalanceAlgorithm;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterModel;
-import org.apache.helix.controller.rebalancer.waged.model.OptimalAssignment;
-import org.apache.helix.model.ResourceAssignment;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The algorithm is based on a given set of constraints
- * - HardConstraint: Approve or deny the assignment given its condition, any assignment cannot
- * bypass any "hard constraint"
- * - SoftConstraint: Evaluate the assignment by points/rewards/scores, a higher point means a better
- * assignment
- * The goal is to accumulate the most points(rewards) from "soft constraints" while avoiding any
- * "hard constraints"
- */
-class ConstraintBasedAlgorithm implements RebalanceAlgorithm {
- private static final Logger LOG = LoggerFactory.getLogger(ConstraintBasedAlgorithm.class);
- private final List<HardConstraint> _hardConstraints;
- private final Map<SoftConstraint, Float> _softConstraints;
-
- ConstraintBasedAlgorithm(List<HardConstraint> hardConstraints,
- Map<SoftConstraint, Float> softConstraints) {
- _hardConstraints = hardConstraints;
- _softConstraints = softConstraints;
- }
-
- @Override
- public OptimalAssignment calculate(ClusterModel clusterModel)
- throws HelixRebalanceException {
- OptimalAssignment optimalAssignment = new OptimalAssignment();
- List<AssignableNode> nodes = new ArrayList<>(clusterModel.getAssignableNodes().values());
- Set<String> busyInstances =
- getBusyInstances(clusterModel.getContext().getBestPossibleAssignment().values());
- // Sort the replicas so the input is stable for the greedy algorithm.
- // For the other algorithm implementation, this sorting could be unnecessary.
- for (AssignableReplica replica : getOrderedAssignableReplica(clusterModel)) {
- Optional<AssignableNode> maybeBestNode =
- getNodeWithHighestPoints(replica, nodes, clusterModel.getContext(), busyInstances,
- optimalAssignment);
- // stop immediately if any replica cannot find best assignable node
- if (optimalAssignment.hasAnyFailure()) {
- String errorMessage = String
- .format("Unable to find any available candidate node for partition %s; Fail reasons: %s",
- replica.getPartitionName(), optimalAssignment.getFailures());
- throw new HelixRebalanceException(errorMessage,
- HelixRebalanceException.Type.FAILED_TO_CALCULATE);
- }
- maybeBestNode.ifPresent(node -> clusterModel
- .assign(replica.getResourceName(), replica.getPartitionName(), replica.getReplicaState(),
- node.getInstanceName()));
- }
- optimalAssignment.updateAssignments(clusterModel);
- return optimalAssignment;
- }
-
- private Optional<AssignableNode> getNodeWithHighestPoints(AssignableReplica replica,
- List<AssignableNode> assignableNodes, ClusterContext clusterContext,
- Set<String> busyInstances, OptimalAssignment optimalAssignment) {
- Map<AssignableNode, List<HardConstraint>> hardConstraintFailures = new ConcurrentHashMap<>();
- List<AssignableNode> candidateNodes = assignableNodes.parallelStream().filter(candidateNode -> {
- boolean isValid = true;
- // need to record all the failure reasons and it gives us the ability to debug/fix the runtime
- // cluster environment
- for (HardConstraint hardConstraint : _hardConstraints) {
- if (!hardConstraint.isAssignmentValid(candidateNode, replica, clusterContext)) {
- hardConstraintFailures.computeIfAbsent(candidateNode, node -> new ArrayList<>())
- .add(hardConstraint);
- isValid = false;
- }
- }
- return isValid;
- }).collect(Collectors.toList());
-
- if (candidateNodes.isEmpty()) {
- optimalAssignment.recordAssignmentFailure(replica,
- Maps.transformValues(hardConstraintFailures, this::convertFailureReasons));
- return Optional.empty();
- }
-
- return candidateNodes.parallelStream().map(node -> new HashMap.SimpleEntry<>(node,
- getAssignmentNormalizedScore(node, replica, clusterContext)))
- .max((nodeEntry1, nodeEntry2) -> {
- int scoreCompareResult = nodeEntry1.getValue().compareTo(nodeEntry2.getValue());
- if (scoreCompareResult == 0) {
- // If the evaluation scores of 2 nodes are the same, the algorithm assigns the replica
- // to the idle node first.
- int idleScore1 = busyInstances.contains(nodeEntry1.getKey().getInstanceName()) ? 0 : 1;
- int idleScore2 = busyInstances.contains(nodeEntry2.getKey().getInstanceName()) ? 0 : 1;
- return idleScore1 - idleScore2;
- } else {
- return scoreCompareResult;
- }
- }).map(Map.Entry::getKey);
- }
-
- private double getAssignmentNormalizedScore(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- double sum = 0;
- for (Map.Entry<SoftConstraint, Float> softConstraintEntry : _softConstraints.entrySet()) {
- SoftConstraint softConstraint = softConstraintEntry.getKey();
- float weight = softConstraintEntry.getValue();
- if (weight != 0) {
- // Skip calculating zero weighted constraints.
- sum += weight * softConstraint.getAssignmentNormalizedScore(node, replica, clusterContext);
- }
- }
- return sum;
- }
-
- private List<String> convertFailureReasons(List<HardConstraint> hardConstraints) {
- return hardConstraints.stream().map(HardConstraint::getDescription)
- .collect(Collectors.toList());
- }
-
- private List<AssignableReplica> getOrderedAssignableReplica(ClusterModel clusterModel) {
- Map<String, Set<AssignableReplica>> replicasByResource = clusterModel.getAssignableReplicaMap();
- List<AssignableReplica> orderedAssignableReplicas =
- replicasByResource.values().stream().flatMap(replicas -> replicas.stream())
- .collect(Collectors.toList());
-
- Map<String, ResourceAssignment> bestPossibleAssignment =
- clusterModel.getContext().getBestPossibleAssignment();
- Map<String, ResourceAssignment> baselineAssignment =
- clusterModel.getContext().getBaselineAssignment();
-
- Map<String, Integer> replicaHashCodeMap = orderedAssignableReplicas.parallelStream().collect(
- Collectors.toMap(AssignableReplica::toString,
- replica -> Objects.hash(replica.toString(), clusterModel.getAssignableNodes().keySet()),
- (hash1, hash2) -> hash2));
-
- // 1. Sort according if the assignment exists in the best possible and/or baseline assignment
- // 2. Sort according to the state priority. Note that prioritizing the top state is required.
- // Or the greedy algorithm will unnecessarily shuffle the states between replicas.
- // 3. Sort according to the resource/partition name.
- orderedAssignableReplicas.sort((replica1, replica2) -> {
- String resourceName1 = replica1.getResourceName();
- String resourceName2 = replica2.getResourceName();
- if (bestPossibleAssignment.containsKey(resourceName1) == bestPossibleAssignment
- .containsKey(resourceName2)) {
- if (baselineAssignment.containsKey(resourceName1) == baselineAssignment
- .containsKey(resourceName2)) {
- // If both assignment states have/not have the resource assignment the same,
- // compare for additional dimensions.
- int statePriority1 = replica1.getStatePriority();
- int statePriority2 = replica2.getStatePriority();
- if (statePriority1 == statePriority2) {
- // If state priorities are the same, try to randomize the replicas order. Otherwise,
- // the same replicas might always be moved in each rebalancing. This is because their
- // placement calculating will always happen at the critical moment while the cluster is
- // almost close to the expected utilization.
- //
- // Note that to ensure the algorithm is deterministic with the same inputs, do not use
- // Random functions here. Use hashcode based on the cluster topology information to get
- // a controlled randomized order is good enough.
- Integer replicaHash1 = replicaHashCodeMap.get(replica1.toString());
- Integer replicaHash2 = replicaHashCodeMap.get(replica2.toString());
- if (!replicaHash1.equals(replicaHash2)) {
- return replicaHash1.compareTo(replicaHash2);
- } else {
- // In case of hash collision, return order according to the name.
- return replica1.toString().compareTo(replica2.toString());
- }
- } else {
- // Note we shall prioritize the replica with a higher state priority,
- // the smaller priority number means higher priority.
- return statePriority1 - statePriority2;
- }
- } else {
- // If the baseline assignment contains the assignment, prioritize the replica.
- return baselineAssignment.containsKey(resourceName1) ? -1 : 1;
- }
- } else {
- // If the best possible assignment contains the assignment, prioritize the replica.
- return bestPossibleAssignment.containsKey(resourceName1) ? -1 : 1;
- }
- });
- return orderedAssignableReplicas;
- }
-
- /**
- * @param assignments A collection of resource replicas assignment.
- * @return A set of instance names that have at least one replica assigned in the input assignments.
- */
- private Set<String> getBusyInstances(Collection<ResourceAssignment> assignments) {
- return assignments.stream().flatMap(
- resourceAssignment -> resourceAssignment.getRecord().getMapFields().values().stream()
- .flatMap(instanceStateMap -> instanceStateMap.keySet().stream())
- .collect(Collectors.toSet()).stream()).collect(Collectors.toSet());
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ConstraintBasedAlgorithmFactory.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ConstraintBasedAlgorithmFactory.java
deleted file mode 100644
index 934bfa7..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ConstraintBasedAlgorithmFactory.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-import org.apache.helix.HelixManagerProperties;
-import org.apache.helix.SystemPropertyKeys;
-import org.apache.helix.controller.rebalancer.waged.RebalanceAlgorithm;
-import org.apache.helix.model.ClusterConfig;
-
-/**
- * The factory class to create an instance of {@link ConstraintBasedAlgorithm}
- */
-public class ConstraintBasedAlgorithmFactory {
- private static final Map<String, Float> MODEL = new HashMap<String, Float>() {
- {
- // The default setting
- put(PartitionMovementConstraint.class.getSimpleName(), 2f);
- put(InstancePartitionsCountConstraint.class.getSimpleName(), 1f);
- put(ResourcePartitionAntiAffinityConstraint.class.getSimpleName(), 1f);
- put(ResourceTopStateAntiAffinityConstraint.class.getSimpleName(), 3f);
- put(MaxCapacityUsageInstanceConstraint.class.getSimpleName(), 5f);
- }
- };
-
- static {
- Properties properties =
- new HelixManagerProperties(SystemPropertyKeys.SOFT_CONSTRAINT_WEIGHTS).getProperties();
- // overwrite the default value with data load from property file
- properties.forEach((constraintName, weight) -> MODEL.put(String.valueOf(constraintName),
- Float.valueOf(String.valueOf(weight))));
- }
-
- public static RebalanceAlgorithm getInstance(
- Map<ClusterConfig.GlobalRebalancePreferenceKey, Integer> preferences) {
- List<HardConstraint> hardConstraints =
- ImmutableList.of(new FaultZoneAwareConstraint(), new NodeCapacityConstraint(),
- new ReplicaActivateConstraint(), new NodeMaxPartitionLimitConstraint(),
- new ValidGroupTagConstraint(), new SamePartitionOnInstanceConstraint());
-
- int evennessPreference =
- preferences.getOrDefault(ClusterConfig.GlobalRebalancePreferenceKey.EVENNESS, 1);
- int movementPreference =
- preferences.getOrDefault(ClusterConfig.GlobalRebalancePreferenceKey.LESS_MOVEMENT, 1);
-
- List<SoftConstraint> softConstraints = ImmutableList
- .of(new PartitionMovementConstraint(), new InstancePartitionsCountConstraint(),
- new ResourcePartitionAntiAffinityConstraint(),
- new ResourceTopStateAntiAffinityConstraint(), new MaxCapacityUsageInstanceConstraint());
- Map<SoftConstraint, Float> softConstraintsWithWeight = Maps.toMap(softConstraints, key -> {
- String name = key.getClass().getSimpleName();
- float weight = MODEL.get(name);
- return name.equals(PartitionMovementConstraint.class.getSimpleName()) ?
- movementPreference * weight : evennessPreference * weight;
- });
-
- return new ConstraintBasedAlgorithm(hardConstraints, softConstraintsWithWeight);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/FaultZoneAwareConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/FaultZoneAwareConstraint.java
deleted file mode 100644
index c33419e..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/FaultZoneAwareConstraint.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-class FaultZoneAwareConstraint extends HardConstraint {
-
- @Override
- boolean isAssignmentValid(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- if (!node.hasFaultZone()) {
- return true;
- }
- return !clusterContext
- .getPartitionsForResourceAndFaultZone(replica.getResourceName(), node.getFaultZone())
- .contains(replica.getPartitionName());
- }
-
- @Override
- String getDescription() {
- return "A fault zone cannot contain more than 1 replica of same partition";
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/HardConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/HardConstraint.java
deleted file mode 100644
index f544d4b..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/HardConstraint.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-/**
- * Evaluate a partition allocation proposal and return YES or NO based on the cluster context.
- * Any proposal fails one or more hard constraints will be rejected.
- */
-abstract class HardConstraint {
-
- /**
- * Check if the replica could be assigned to the node
- * @return True if the proposed assignment is valid; False otherwise
- */
- abstract boolean isAssignmentValid(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext);
-
- /**
- * Return class name by default as description if it's explanatory enough, child class could override
- * the method and add more detailed descriptions
- * @return The detailed description of hard constraint
- */
- String getDescription() {
- return getClass().getName();
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/InstancePartitionsCountConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/InstancePartitionsCountConstraint.java
deleted file mode 100644
index 948a7d0..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/InstancePartitionsCountConstraint.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-/**
- * Evaluate by instance's current partition count versus estimated max partition count
- * Intuitively, Encourage the assignment if the instance's occupancy rate is below average;
- * Discourage the assignment if the instance's occupancy rate is above average
- * The normalized score will be within [0, 1]
- */
-class InstancePartitionsCountConstraint extends UsageSoftConstraint {
-
- @Override
- protected double getAssignmentScore(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- int estimatedMaxPartitionCount = clusterContext.getEstimatedMaxPartitionCount();
- int currentPartitionCount = node.getAssignedReplicaCount();
- return computeUtilizationScore(estimatedMaxPartitionCount, currentPartitionCount);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/MaxCapacityUsageInstanceConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/MaxCapacityUsageInstanceConstraint.java
deleted file mode 100644
index 8f41f5e..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/MaxCapacityUsageInstanceConstraint.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-/**
- * The constraint evaluates the score by checking the max used capacity key out of all the capacity
- * keys.
- * The higher the maximum usage value for the capacity key, the lower the score will be, implying
- * that it is that much less desirable to assign anything on the given node.
- * It is a greedy approach since it evaluates only on the most used capacity key.
- */
-class MaxCapacityUsageInstanceConstraint extends UsageSoftConstraint {
-
- @Override
- protected double getAssignmentScore(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- float estimatedMaxUtilization = clusterContext.getEstimatedMaxUtilization();
- float projectedHighestUtilization = node.getProjectedHighestUtilization(replica.getCapacity());
- return computeUtilizationScore(estimatedMaxUtilization, projectedHighestUtilization);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/NodeCapacityConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/NodeCapacityConstraint.java
deleted file mode 100644
index 827d6ce..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/NodeCapacityConstraint.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import java.util.Map;
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-class NodeCapacityConstraint extends HardConstraint {
-
- @Override
- boolean isAssignmentValid(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- Map<String, Integer> nodeCapacity = node.getRemainingCapacity();
- Map<String, Integer> replicaCapacity = replica.getCapacity();
-
- for (String key : replicaCapacity.keySet()) {
- if (nodeCapacity.containsKey(key)) {
- if (nodeCapacity.get(key) < replicaCapacity.get(key)) {
- return false;
- }
- }
- }
- return true;
- }
-
- @Override
- String getDescription() {
- return "Node has insufficient capacity";
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/NodeMaxPartitionLimitConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/NodeMaxPartitionLimitConstraint.java
deleted file mode 100644
index cda5329..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/NodeMaxPartitionLimitConstraint.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-class NodeMaxPartitionLimitConstraint extends HardConstraint {
-
- @Override
- boolean isAssignmentValid(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- boolean exceedMaxPartitionLimit =
- node.getMaxPartition() < 0 || node.getAssignedReplicaCount() < node.getMaxPartition();
- boolean exceedResourceMaxPartitionLimit = replica.getResourceMaxPartitionsPerInstance() < 0
- || node.getAssignedPartitionsByResource(replica.getResourceName()).size() < replica
- .getResourceMaxPartitionsPerInstance();
- return exceedMaxPartitionLimit && exceedResourceMaxPartitionLimit;
- }
-
- @Override
- String getDescription() {
- return "Cannot exceed the maximum number of partitions limitation on node";
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/PartitionMovementConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/PartitionMovementConstraint.java
deleted file mode 100644
index dc19c19..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/PartitionMovementConstraint.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import java.util.Collections;
-import java.util.Map;
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-import org.apache.helix.model.Partition;
-import org.apache.helix.model.ResourceAssignment;
-
-/**
- * Evaluate the proposed assignment according to the potential partition movements cost.
- * The cost is evaluated based on the difference between the old assignment and the new assignment.
- * In detail, we consider the following two previous assignments as the base.
- * - Baseline assignment that is calculated regardless of the node state (online/offline).
- * - Previous Best Possible assignment.
- * Any change to these two assignments will increase the partition movements cost, so that the
- * evaluated score will become lower.
- */
-class PartitionMovementConstraint extends SoftConstraint {
- private static final double MAX_SCORE = 1f;
- private static final double MIN_SCORE = 0f;
- //TODO: these factors will be tuned based on user's preference
- // This factor indicates the default score that is evaluated if only partition allocation matches
- // (states are different).
- private static final double ALLOCATION_MATCH_FACTOR = 0.5;
-
- PartitionMovementConstraint() {
- super(MAX_SCORE, MIN_SCORE);
- }
-
- @Override
- protected double getAssignmentScore(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- // Prioritize the previous Best Possible assignment
- Map<String, String> bestPossibleAssignment =
- getStateMap(replica, clusterContext.getBestPossibleAssignment());
- if (!bestPossibleAssignment.isEmpty()) {
- return calculateAssignmentScale(node, replica, bestPossibleAssignment);
- }
- // else, compare the baseline only if the best possible assignment does not contain the replica
- Map<String, String> baselineAssignment =
- getStateMap(replica, clusterContext.getBaselineAssignment());
- if (!baselineAssignment.isEmpty()) {
- return calculateAssignmentScale(node, replica, baselineAssignment);
- }
- return 0;
- }
-
- private Map<String, String> getStateMap(AssignableReplica replica,
- Map<String, ResourceAssignment> assignment) {
- String resourceName = replica.getResourceName();
- String partitionName = replica.getPartitionName();
- if (assignment == null || !assignment.containsKey(resourceName)) {
- return Collections.emptyMap();
- }
- return assignment.get(resourceName).getReplicaMap(new Partition(partitionName));
- }
-
- private double calculateAssignmentScale(AssignableNode node, AssignableReplica replica,
- Map<String, String> instanceToStateMap) {
- String instanceName = node.getInstanceName();
- if (!instanceToStateMap.containsKey(instanceName)) {
- return 0;
- } else {
- return (instanceToStateMap.get(instanceName).equals(replica.getReplicaState()) ? 1 :
- ALLOCATION_MATCH_FACTOR);
- }
- }
-
- @Override
- protected NormalizeFunction getNormalizeFunction() {
- // PartitionMovementConstraint already scale the score properly.
- return (score) -> score;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ReplicaActivateConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ReplicaActivateConstraint.java
deleted file mode 100644
index 9152efe..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ReplicaActivateConstraint.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import java.util.List;
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-class ReplicaActivateConstraint extends HardConstraint {
- @Override
- boolean isAssignmentValid(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- List<String> disabledPartitions =
- node.getDisabledPartitionsMap().get(replica.getResourceName());
- return disabledPartitions == null || !disabledPartitions.contains(replica.getPartitionName());
- }
-
- @Override
- String getDescription() {
- return "Cannot assign the inactive replica";
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ResourcePartitionAntiAffinityConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ResourcePartitionAntiAffinityConstraint.java
deleted file mode 100644
index a3b701f..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ResourcePartitionAntiAffinityConstraint.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-/**
- * This constraint exists to make partitions belonging to the same resource be assigned as far from
- * each other as possible. This is because it is undesirable to have many partitions belonging to
- * the same resource be assigned to the same node to minimize the impact of node failure scenarios.
- * The score is higher the fewer the partitions are on the node belonging to the same resource.
- */
-class ResourcePartitionAntiAffinityConstraint extends UsageSoftConstraint {
- @Override
- protected double getAssignmentScore(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- String resource = replica.getResourceName();
- int curPartitionCountForResource = node.getAssignedPartitionsByResource(resource).size();
- int estimatedMaxPartitionCountForResource =
- clusterContext.getEstimatedMaxPartitionByResource(resource);
- return computeUtilizationScore(estimatedMaxPartitionCountForResource,
- curPartitionCountForResource);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ResourceTopStateAntiAffinityConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ResourceTopStateAntiAffinityConstraint.java
deleted file mode 100644
index f0f9e13..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ResourceTopStateAntiAffinityConstraint.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-/**
- * Evaluate the proposed assignment according to the top state replication count on the instance.
- * The higher number the number of top state partitions assigned to the instance, the lower the
- * score, vice versa.
- */
-class ResourceTopStateAntiAffinityConstraint extends UsageSoftConstraint {
- @Override
- protected double getAssignmentScore(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- if (!replica.isReplicaTopState()) {
- // For non top state replica, this constraint is not applicable.
- // So return zero on any assignable node candidate.
- return 0;
- }
- int curTopPartitionCountForResource = node.getAssignedTopStatePartitionsCount();
- int estimatedMaxTopStateCount = clusterContext.getEstimatedMaxTopStateCount();
- return computeUtilizationScore(estimatedMaxTopStateCount, curTopPartitionCountForResource);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/SamePartitionOnInstanceConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/SamePartitionOnInstanceConstraint.java
deleted file mode 100644
index 202e49a..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/SamePartitionOnInstanceConstraint.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-class SamePartitionOnInstanceConstraint extends HardConstraint {
-
- @Override
- boolean isAssignmentValid(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- return !node.getAssignedPartitionsByResource(replica.getResourceName())
- .contains(replica.getPartitionName());
- }
-
- @Override
- String getDescription() {
- return "Same partition of different states cannot co-exist in one instance";
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/SoftConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/SoftConstraint.java
deleted file mode 100644
index 21bed84..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/SoftConstraint.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-/**
- * The "soft" constraint evaluates the optimality of an assignment by giving it a score of a scale
- * of [minScore, maxScore]
- * The higher the score, the better the assignment; Intuitively, the assignment is encouraged.
- * The lower score the score, the worse the assignment; Intuitively, the assignment is penalized.
- */
-abstract class SoftConstraint {
- private final double _maxScore;
- private final double _minScore;
-
- interface NormalizeFunction {
- /**
- * Scale the origin score to a normalized range (0, 1).
- * The purpose is to compare scores between different soft constraints.
- * @param originScore The origin score
- * @return The normalized value between (0, 1)
- */
- double scale(double originScore);
- }
-
- /**
- * Child class customize the min/max score on its own
- * @param maxScore The max score
- * @param minScore The min score
- */
- SoftConstraint(double maxScore, double minScore) {
- _maxScore = maxScore;
- _minScore = minScore;
- }
-
- protected double getMaxScore() {
- return _maxScore;
- }
-
- protected double getMinScore() {
- return _minScore;
- }
-
- /**
- * Evaluate and give a score for an potential assignment partition -> instance
- * Child class only needs to care about how the score is implemented
- * @return The score of the assignment in double value
- */
- protected abstract double getAssignmentScore(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext);
-
- /**
- * Evaluate and give a score for an potential assignment partition -> instance
- * It's the only exposed method to the caller
- * @return The score is normalized to be within MinScore and MaxScore
- */
- double getAssignmentNormalizedScore(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- return getNormalizeFunction().scale(getAssignmentScore(node, replica, clusterContext));
- }
-
- /**
- * The default scaler function that squashes any score within (min_score, max_score) to (0, 1);
- * Child class could override the method and customize the method on its own
- * @return The MinMaxScaler instance by default
- */
- protected NormalizeFunction getNormalizeFunction() {
- return (score) -> (score - getMinScore()) / (getMaxScore() - getMinScore());
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/UsageSoftConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/UsageSoftConstraint.java
deleted file mode 100644
index c8bc521..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/UsageSoftConstraint.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import org.apache.commons.math3.analysis.function.Sigmoid;
-
-/**
- * The soft constraint that evaluates the assignment proposal based on usage.
- */
-abstract class UsageSoftConstraint extends SoftConstraint {
- private static final double MAX_SCORE = 1f;
- private static final double MIN_SCORE = 0f;
- /**
- * Alpha is used to adjust the curve of sigmoid function.
- * Intuitively, this is for tolerating the inaccuracy of the estimation.
- * Ideally, if we have the prefect estimation, we can use a segmented function here, which
- * scores the assignment with 1.0 if projected usage is below the estimation, and scores 0.0
- * if the projected usage exceeds the estimation. However, in reality, it is hard to get a
- * prefect estimation. With the curve of sigmoid, the algorithm reacts differently and
- * reasonally even the usage is a little bit more or less than the estimation for a certain
- * extend.
- * As tested, when we have the input number which surrounds 1, the default alpha value will
- * ensure a curve that has sigmoid(0.95) = 0.90, sigmoid(1.05) = 0.1. Meaning the constraint
- * can handle the estimation inaccuracy of +-5%.
- * To adjust the curve:
- * 1. Smaller alpha will increase the curve's scope. So the function will be handler a wilder
- * range of inaccuracy. However, the downside is more random movements since the evenness
- * score would be more changable and nondefinitive.
- * 2. Larger alpha will decrease the curve's scope. In that case, we might want to change to
- * use segmented function so as to speed up the algorthm.
- **/
- private static final int DEFAULT_ALPHA = 44;
- private static final Sigmoid SIGMOID = new Sigmoid();
-
- UsageSoftConstraint() {
- super(MAX_SCORE, MIN_SCORE);
- }
-
- /**
- * Compute the utilization score based on the estimated and current usage numbers.
- * The score = currentUsage / estimatedUsage.
- * In short, a smaller score means better assignment proposal.
- *
- * @param estimatedUsage The estimated usage that is between [0.0, 1.0]
- * @param currentUsage The current usage that is between [0.0, 1.0]
- * @return The score between [0.0, 1.0] that evaluates the utilization.
- */
- protected double computeUtilizationScore(double estimatedUsage, double currentUsage) {
- if (estimatedUsage == 0) {
- return 0;
- }
- return currentUsage / estimatedUsage;
- }
-
- /**
- * Compute evaluation score based on the utilization data.
- * The normalized score is evaluated using a sigmoid function.
- * When the usage is smaller than 1.0, the constraint returns a value that is very close to the
- * max score.
- * When the usage is close or larger than 1.0, the constraint returns a score that is very close
- * to the min score. Note even in this case, more usage will still be assigned with a
- * smaller score.
- */
- @Override
- protected NormalizeFunction getNormalizeFunction() {
- return (score) -> SIGMOID.value(-(score - 1) * DEFAULT_ALPHA) * (MAX_SCORE - MIN_SCORE);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ValidGroupTagConstraint.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ValidGroupTagConstraint.java
deleted file mode 100644
index e31864f..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/constraints/ValidGroupTagConstraint.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.constraints;
-
-/*
- * 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.
- */
-
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterContext;
-
-class ValidGroupTagConstraint extends HardConstraint {
- @Override
- boolean isAssignmentValid(AssignableNode node, AssignableReplica replica,
- ClusterContext clusterContext) {
- if (!replica.hasResourceInstanceGroupTag()) {
- return true;
- }
-
- return node.getInstanceTags().contains(replica.getResourceInstanceGroupTag());
- }
-
- @Override
- String getDescription() {
- return "Instance doesn't have the tag of the replica";
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/AssignableNode.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/AssignableNode.java
deleted file mode 100644
index 06d4976..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/AssignableNode.java
+++ /dev/null
@@ -1,374 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.model;
-
-/*
- * 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.
- */
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import org.apache.helix.HelixException;
-import org.apache.helix.controller.rebalancer.util.WagedValidationUtil;
-import org.apache.helix.model.ClusterConfig;
-import org.apache.helix.model.InstanceConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-/**
- * This class represents a possible allocation of the replication.
- * Note that any usage updates to the AssignableNode are not thread safe.
- */
-public class AssignableNode implements Comparable<AssignableNode> {
- private static final Logger LOG = LoggerFactory.getLogger(AssignableNode.class.getName());
-
- // Immutable Instance Properties
- private final String _instanceName;
- private final String _faultZone;
- // maximum number of the partitions that can be assigned to the instance.
- private final int _maxPartition;
- private final ImmutableSet<String> _instanceTags;
- private final ImmutableMap<String, List<String>> _disabledPartitionsMap;
- private final ImmutableMap<String, Integer> _maxAllowedCapacity;
-
- // Mutable (Dynamic) Instance Properties
- // A map of <resource name, <partition name, replica>> that tracks the replicas assigned to the
- // node.
- private Map<String, Map<String, AssignableReplica>> _currentAssignedReplicaMap;
- // A map of <capacity key, capacity value> that tracks the current available node capacity
- private Map<String, Integer> _remainingCapacity;
-
- /**
- * Update the node with a ClusterDataCache. This resets the current assignment and recalculates
- * currentCapacity.
- * NOTE: While this is required to be used in the constructor, this can also be used when the
- * clusterCache needs to be
- * refreshed. This is under the assumption that the capacity mappings of InstanceConfig and
- * ResourceConfig could
- * subject to change. If the assumption is no longer true, this function should become private.
- */
- AssignableNode(ClusterConfig clusterConfig, InstanceConfig instanceConfig, String instanceName) {
- _instanceName = instanceName;
- Map<String, Integer> instanceCapacity = fetchInstanceCapacity(clusterConfig, instanceConfig);
- _faultZone = computeFaultZone(clusterConfig, instanceConfig);
- _instanceTags = ImmutableSet.copyOf(instanceConfig.getTags());
- _disabledPartitionsMap = ImmutableMap.copyOf(instanceConfig.getDisabledPartitionsMap());
- // make a copy of max capacity
- _maxAllowedCapacity = ImmutableMap.copyOf(instanceCapacity);
- _remainingCapacity = new HashMap<>(instanceCapacity);
- _maxPartition = clusterConfig.getMaxPartitionsPerInstance();
- _currentAssignedReplicaMap = new HashMap<>();
- }
-
- /**
- * This function should only be used to assign a set of new partitions that are not allocated on
- * this node. It's because the any exception could occur at the middle of batch assignment and the
- * previous finished assignment cannot be reverted
- * Using this function avoids the overhead of updating capacity repeatedly.
- */
- void assignInitBatch(Collection<AssignableReplica> replicas) {
- Map<String, Integer> totalPartitionCapacity = new HashMap<>();
- for (AssignableReplica replica : replicas) {
- // TODO: the exception could occur in the middle of for loop and the previous added records cannot be reverted
- addToAssignmentRecord(replica);
- // increment the capacity requirement according to partition's capacity configuration.
- for (Map.Entry<String, Integer> capacity : replica.getCapacity().entrySet()) {
- totalPartitionCapacity.compute(capacity.getKey(),
- (key, totalValue) -> (totalValue == null) ? capacity.getValue()
- : totalValue + capacity.getValue());
- }
- }
-
- // Update the global state after all single replications' calculation is done.
- for (String capacityKey : totalPartitionCapacity.keySet()) {
- updateRemainingCapacity(capacityKey, totalPartitionCapacity.get(capacityKey));
- }
- }
-
- /**
- * Assign a replica to the node.
- * @param assignableReplica - the replica to be assigned
- */
- void assign(AssignableReplica assignableReplica) {
- addToAssignmentRecord(assignableReplica);
- assignableReplica.getCapacity().entrySet().stream()
- .forEach(capacity -> updateRemainingCapacity(capacity.getKey(), capacity.getValue()));
- }
-
- /**
- * Release a replica from the node.
- * If the replication is not on this node, the assignable node is not updated.
- * @param replica - the replica to be released
- */
- void release(AssignableReplica replica)
- throws IllegalArgumentException {
- String resourceName = replica.getResourceName();
- String partitionName = replica.getPartitionName();
-
- // Check if the release is necessary
- if (!_currentAssignedReplicaMap.containsKey(resourceName)) {
- LOG.warn("Resource {} is not on node {}. Ignore the release call.", resourceName,
- getInstanceName());
- return;
- }
-
- Map<String, AssignableReplica> partitionMap = _currentAssignedReplicaMap.get(resourceName);
- if (!partitionMap.containsKey(partitionName) || !partitionMap.get(partitionName)
- .equals(replica)) {
- LOG.warn("Replica {} is not assigned to node {}. Ignore the release call.",
- replica.toString(), getInstanceName());
- return;
- }
-
- AssignableReplica removedReplica = partitionMap.remove(partitionName);
- removedReplica.getCapacity().entrySet().stream()
- .forEach(entry -> updateRemainingCapacity(entry.getKey(), -1 * entry.getValue()));
- }
-
- /**
- * @return A set of all assigned replicas on the node.
- */
- Set<AssignableReplica> getAssignedReplicas() {
- return _currentAssignedReplicaMap.values().stream()
- .flatMap(replicaMap -> replicaMap.values().stream()).collect(Collectors.toSet());
- }
-
- /**
- * @return The current assignment in a map of <resource name, set of partition names>
- */
- Map<String, Set<String>> getAssignedPartitionsMap() {
- Map<String, Set<String>> assignmentMap = new HashMap<>();
- for (String resourceName : _currentAssignedReplicaMap.keySet()) {
- assignmentMap.put(resourceName, _currentAssignedReplicaMap.get(resourceName).keySet());
- }
- return assignmentMap;
- }
-
- /**
- * @param resource Resource name
- * @return A set of the current assigned replicas' partition names in the specified resource.
- */
- public Set<String> getAssignedPartitionsByResource(String resource) {
- return _currentAssignedReplicaMap.getOrDefault(resource, Collections.emptyMap()).keySet();
- }
-
- /**
- * @param resource Resource name
- * @return A set of the current assigned replicas' partition names with the top state in the
- * specified resource.
- */
- Set<String> getAssignedTopStatePartitionsByResource(String resource) {
- return _currentAssignedReplicaMap.getOrDefault(resource, Collections.emptyMap()).entrySet()
- .stream().filter(partitionEntry -> partitionEntry.getValue().isReplicaTopState())
- .map(partitionEntry -> partitionEntry.getKey()).collect(Collectors.toSet());
- }
-
- /**
- * @return The total count of assigned top state partitions.
- */
- public int getAssignedTopStatePartitionsCount() {
- return (int) _currentAssignedReplicaMap.values().stream()
- .flatMap(replicaMap -> replicaMap.values().stream())
- .filter(AssignableReplica::isReplicaTopState).count();
- }
-
- /**
- * @return The total count of assigned replicas.
- */
- public int getAssignedReplicaCount() {
- return _currentAssignedReplicaMap.values().stream().mapToInt(Map::size).sum();
- }
-
- /**
- * @return The current available capacity.
- */
- public Map<String, Integer> getRemainingCapacity() {
- return _remainingCapacity;
- }
-
- /**
- * @return A map of <capacity category, capacity number> that describes the max capacity of the
- * node.
- */
- public Map<String, Integer> getMaxCapacity() {
- return _maxAllowedCapacity;
- }
-
- /**
- * Return the most concerning capacity utilization number for evenly partition assignment.
- * The method dynamically calculates the projected highest utilization number among all the
- * capacity categories assuming the new capacity usage is added to the node.
- * For example, if the current node usage is {CPU: 0.9, MEM: 0.4, DISK: 0.6}. Then this call shall
- * return 0.9.
- * @param newUsage the proposed new additional capacity usage.
- * @return The highest utilization number of the node among all the capacity category.
- */
- public float getProjectedHighestUtilization(Map<String, Integer> newUsage) {
- float highestCapacityUtilization = 0;
- for (String capacityKey : _maxAllowedCapacity.keySet()) {
- float capacityValue = _maxAllowedCapacity.get(capacityKey);
- float utilization = (capacityValue - _remainingCapacity.get(capacityKey) + newUsage
- .getOrDefault(capacityKey, 0)) / capacityValue;
- highestCapacityUtilization = Math.max(highestCapacityUtilization, utilization);
- }
- return highestCapacityUtilization;
- }
-
- public String getInstanceName() {
- return _instanceName;
- }
-
- public Set<String> getInstanceTags() {
- return _instanceTags;
- }
-
- public String getFaultZone() {
- return _faultZone;
- }
-
- public boolean hasFaultZone() {
- return _faultZone != null;
- }
-
- /**
- * @return A map of <resource name, set of partition names> contains all the partitions that are
- * disabled on the node.
- */
- public Map<String, List<String>> getDisabledPartitionsMap() {
- return _disabledPartitionsMap;
- }
-
- /**
- * @return The max partition count that are allowed to be allocated on the node.
- */
- public int getMaxPartition() {
- return _maxPartition;
- }
-
- /**
- * Computes the fault zone id based on the domain and fault zone type when topology is enabled.
- * For example, when
- * the domain is "zone=2, instance=testInstance" and the fault zone type is "zone", this function
- * returns "2".
- * If cannot find the fault zone type, this function leaves the fault zone id as the instance name.
- * Note the WAGED rebalancer does not require full topology tree to be created. So this logic is
- * simpler than the CRUSH based rebalancer.
- */
- private String computeFaultZone(ClusterConfig clusterConfig, InstanceConfig instanceConfig) {
- if (!clusterConfig.isTopologyAwareEnabled()) {
- // Instance name is the default fault zone if topology awareness is false.
- return instanceConfig.getInstanceName();
- }
- String topologyStr = clusterConfig.getTopology();
- String faultZoneType = clusterConfig.getFaultZoneType();
- if (topologyStr == null || faultZoneType == null) {
- LOG.debug("Topology configuration is not complete. Topology define: {}, Fault Zone Type: {}",
- topologyStr, faultZoneType);
- // Use the instance name, or the deprecated ZoneId field (if exists) as the default fault
- // zone.
- String zoneId = instanceConfig.getZoneId();
- return zoneId == null ? instanceConfig.getInstanceName() : zoneId;
- } else {
- // Get the fault zone information from the complete topology definition.
- String[] topologyKeys = topologyStr.trim().split("/");
- if (topologyKeys.length == 0 || Arrays.stream(topologyKeys)
- .noneMatch(type -> type.equals(faultZoneType))) {
- throw new HelixException(
- "The configured topology definition is empty or does not contain the fault zone type.");
- }
-
- Map<String, String> domainAsMap = instanceConfig.getDomainAsMap();
- StringBuilder faultZoneStringBuilder = new StringBuilder();
- for (String key : topologyKeys) {
- if (!key.isEmpty()) {
- // if a key does not exist in the instance domain config, apply the default domain value.
- faultZoneStringBuilder.append(domainAsMap.getOrDefault(key, "Default_" + key));
- if (key.equals(faultZoneType)) {
- break;
- } else {
- faultZoneStringBuilder.append('/');
- }
- }
- }
- return faultZoneStringBuilder.toString();
- }
- }
-
- /**
- * @throws HelixException if the replica has already been assigned to the node.
- */
- private void addToAssignmentRecord(AssignableReplica replica) {
- String resourceName = replica.getResourceName();
- String partitionName = replica.getPartitionName();
- if (_currentAssignedReplicaMap.containsKey(resourceName) && _currentAssignedReplicaMap
- .get(resourceName).containsKey(partitionName)) {
- throw new HelixException(String
- .format("Resource %s already has a replica with state %s from partition %s on node %s",
- replica.getResourceName(), replica.getReplicaState(), replica.getPartitionName(),
- getInstanceName()));
- } else {
- _currentAssignedReplicaMap.computeIfAbsent(resourceName, key -> new HashMap<>())
- .put(partitionName, replica);
- }
- }
-
- private void updateRemainingCapacity(String capacityKey, int usage) {
- if (!_remainingCapacity.containsKey(capacityKey)) {
- //if the capacityKey belongs to replicas does not exist in the instance's capacity,
- // it will be treated as if it has unlimited capacity of that capacityKey
- return;
- }
- _remainingCapacity.put(capacityKey, _remainingCapacity.get(capacityKey) - usage);
- }
-
- /**
- * Get and validate the instance capacity from instance config.
- * @throws HelixException if any required capacity key is not configured in the instance config.
- */
- private Map<String, Integer> fetchInstanceCapacity(ClusterConfig clusterConfig,
- InstanceConfig instanceConfig) {
- Map<String, Integer> instanceCapacity =
- WagedValidationUtil.validateAndGetInstanceCapacity(clusterConfig, instanceConfig);
- // Remove all the non-required capacity items from the map.
- instanceCapacity.keySet().retainAll(clusterConfig.getInstanceCapacityKeys());
- return instanceCapacity;
- }
-
- @Override
- public int hashCode() {
- return _instanceName.hashCode();
- }
-
- @Override
- public int compareTo(AssignableNode o) {
- return _instanceName.compareTo(o.getInstanceName());
- }
-
- @Override
- public String toString() {
- return _instanceName;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/AssignableReplica.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/AssignableReplica.java
deleted file mode 100644
index fdcc03a..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/AssignableReplica.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.model;
-
-/*
- * 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.
- */
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.helix.HelixException;
-import org.apache.helix.controller.rebalancer.util.WagedValidationUtil;
-import org.apache.helix.model.ClusterConfig;
-import org.apache.helix.model.ResourceConfig;
-import org.apache.helix.model.StateModelDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This class represents a partition replication that needs to be allocated.
- */
-public class AssignableReplica implements Comparable<AssignableReplica> {
- private static final Logger LOG = LoggerFactory.getLogger(AssignableReplica.class);
-
- private final String _replicaKey;
- private final String _partitionName;
- private final String _resourceName;
- private final String _resourceInstanceGroupTag;
- private final int _resourceMaxPartitionsPerInstance;
- private final Map<String, Integer> _capacityUsage;
- // The priority of the replica's state
- private final int _statePriority;
- // The state of the replica
- private final String _replicaState;
-
- /**
- * @param clusterConfig The cluster config.
- * @param resourceConfig The resource config for the resource which contains the replication.
- * @param partitionName The replication's partition name.
- * @param replicaState The state of the replication.
- * @param statePriority The priority of the replication's state.
- */
- AssignableReplica(ClusterConfig clusterConfig, ResourceConfig resourceConfig,
- String partitionName, String replicaState, int statePriority) {
- _partitionName = partitionName;
- _replicaState = replicaState;
- _statePriority = statePriority;
- _resourceName = resourceConfig.getResourceName();
- _capacityUsage = fetchCapacityUsage(partitionName, resourceConfig, clusterConfig);
- _resourceInstanceGroupTag = resourceConfig.getInstanceGroupTag();
- _resourceMaxPartitionsPerInstance = resourceConfig.getMaxPartitionsPerInstance();
- _replicaKey = generateReplicaKey(_resourceName, _partitionName,_replicaState);
- }
-
- public Map<String, Integer> getCapacity() {
- return _capacityUsage;
- }
-
- public String getPartitionName() {
- return _partitionName;
- }
-
- public String getReplicaState() {
- return _replicaState;
- }
-
- public boolean isReplicaTopState() {
- return _statePriority == StateModelDefinition.TOP_STATE_PRIORITY;
- }
-
- public int getStatePriority() {
- return _statePriority;
- }
-
- public String getResourceName() {
- return _resourceName;
- }
-
- public String getResourceInstanceGroupTag() {
- return _resourceInstanceGroupTag;
- }
-
- public boolean hasResourceInstanceGroupTag() {
- return _resourceInstanceGroupTag != null && !_resourceInstanceGroupTag.isEmpty();
- }
-
- public int getResourceMaxPartitionsPerInstance() {
- return _resourceMaxPartitionsPerInstance;
- }
-
- @Override
- public String toString() {
- return _replicaKey;
- }
-
- @Override
- public int compareTo(AssignableReplica replica) {
- if (!_resourceName.equals(replica._resourceName)) {
- return _resourceName.compareTo(replica._resourceName);
- }
- if (!_partitionName.equals(replica._partitionName)) {
- return _partitionName.compareTo(replica._partitionName);
- }
- if (!_replicaState.equals(replica._replicaState)) {
- return _replicaState.compareTo(replica._replicaState);
- }
- return 0;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (obj instanceof AssignableReplica) {
- return compareTo((AssignableReplica) obj) == 0;
- } else {
- return false;
- }
- }
-
- public static String generateReplicaKey(String resourceName, String partitionName, String state) {
- return String.format("%s-%s-%s", resourceName, partitionName, state);
- }
-
- /**
- * Parse the resource config for the partition weight.
- */
- private Map<String, Integer> fetchCapacityUsage(String partitionName,
- ResourceConfig resourceConfig, ClusterConfig clusterConfig) {
- Map<String, Map<String, Integer>> capacityMap;
- try {
- capacityMap = resourceConfig.getPartitionCapacityMap();
- } catch (IOException ex) {
- throw new IllegalArgumentException(
- "Invalid partition capacity configuration of resource: " + resourceConfig
- .getResourceName(), ex);
- }
- Map<String, Integer> partitionCapacity = WagedValidationUtil
- .validateAndGetPartitionCapacity(partitionName, resourceConfig, capacityMap, clusterConfig);
- // Remove the non-required capacity items.
- partitionCapacity.keySet().retainAll(clusterConfig.getInstanceCapacityKeys());
- return partitionCapacity;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterContext.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterContext.java
deleted file mode 100644
index 4705be5..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterContext.java
+++ /dev/null
@@ -1,172 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.model;
-
-/*
- * 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.
- */
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apache.helix.HelixException;
-import org.apache.helix.model.ResourceAssignment;
-
-
-/**
- * This class tracks the rebalance-related global cluster status.
- */
-public class ClusterContext {
- // This estimation helps to ensure global partition count evenness
- private final int _estimatedMaxPartitionCount;
- // This estimation helps to ensure global top state replica count evenness
- private final int _estimatedMaxTopStateCount;
- // This estimation helps to ensure per-resource partition count evenness
- private final Map<String, Integer> _estimatedMaxPartitionByResource = new HashMap<>();
- // This estimation helps to ensure global resource usage evenness.
- private final float _estimatedMaxUtilization;
-
- // map{zoneName : map{resourceName : set(partitionNames)}}
- private Map<String, Map<String, Set<String>>> _assignmentForFaultZoneMap = new HashMap<>();
- // Records about the previous assignment
- // <ResourceName, ResourceAssignment contains the baseline assignment>
- private final Map<String, ResourceAssignment> _baselineAssignment;
- // <ResourceName, ResourceAssignment contains the best possible assignment>
- private final Map<String, ResourceAssignment> _bestPossibleAssignment;
-
- /**
- * Construct the cluster context based on the current instance status.
- * @param replicaSet All the partition replicas that are managed by the rebalancer
- * @param nodeSet All the active nodes that are managed by the rebalancer
- */
- ClusterContext(Set<AssignableReplica> replicaSet, Set<AssignableNode> nodeSet,
- Map<String, ResourceAssignment> baselineAssignment, Map<String, ResourceAssignment> bestPossibleAssignment) {
- int instanceCount = nodeSet.size();
- int totalReplicas = 0;
- int totalTopStateReplicas = 0;
- Map<String, Integer> totalUsage = new HashMap<>();
- Map<String, Integer> totalCapacity = new HashMap<>();
-
- for (Map.Entry<String, List<AssignableReplica>> entry : replicaSet.stream()
- .collect(Collectors.groupingBy(AssignableReplica::getResourceName))
- .entrySet()) {
- int replicas = entry.getValue().size();
- totalReplicas += replicas;
-
- int replicaCnt = Math.max(1, estimateAvgReplicaCount(replicas, instanceCount));
- _estimatedMaxPartitionByResource.put(entry.getKey(), replicaCnt);
-
- for (AssignableReplica replica : entry.getValue()) {
- if (replica.isReplicaTopState()) {
- totalTopStateReplicas += 1;
- }
- replica.getCapacity().entrySet().stream().forEach(capacityEntry -> totalUsage
- .compute(capacityEntry.getKey(),
- (k, v) -> (v == null) ? capacityEntry.getValue() : (v + capacityEntry.getValue())));
- }
- }
- nodeSet.stream().forEach(node -> node.getMaxCapacity().entrySet().stream().forEach(
- capacityEntry -> totalCapacity.compute(capacityEntry.getKey(),
- (k, v) -> (v == null) ? capacityEntry.getValue() : (v + capacityEntry.getValue()))));
-
- if (totalCapacity.isEmpty()) {
- // If no capacity is configured, we treat the cluster as fully utilized.
- _estimatedMaxUtilization = 1f;
- } else {
- float estimatedMaxUsage = 0;
- for (String capacityKey : totalCapacity.keySet()) {
- int maxCapacity = totalCapacity.get(capacityKey);
- int usage = totalUsage.getOrDefault(capacityKey, 0);
- float utilization = (maxCapacity == 0) ? 1 : (float) usage / maxCapacity;
- estimatedMaxUsage = Math.max(estimatedMaxUsage, utilization);
- }
- _estimatedMaxUtilization = estimatedMaxUsage;
- }
- _estimatedMaxPartitionCount = estimateAvgReplicaCount(totalReplicas, instanceCount);
- _estimatedMaxTopStateCount = estimateAvgReplicaCount(totalTopStateReplicas, instanceCount);
- _baselineAssignment = baselineAssignment;
- _bestPossibleAssignment = bestPossibleAssignment;
- }
-
- public Map<String, ResourceAssignment> getBaselineAssignment() {
- return _baselineAssignment == null || _baselineAssignment.isEmpty() ? Collections.emptyMap() : _baselineAssignment;
- }
-
- public Map<String, ResourceAssignment> getBestPossibleAssignment() {
- return _bestPossibleAssignment == null || _bestPossibleAssignment.isEmpty() ? Collections.emptyMap()
- : _bestPossibleAssignment;
- }
-
- public Map<String, Map<String, Set<String>>> getAssignmentForFaultZoneMap() {
- return _assignmentForFaultZoneMap;
- }
-
- public int getEstimatedMaxPartitionCount() {
- return _estimatedMaxPartitionCount;
- }
-
- public int getEstimatedMaxPartitionByResource(String resourceName) {
- return _estimatedMaxPartitionByResource.get(resourceName);
- }
-
- public int getEstimatedMaxTopStateCount() {
- return _estimatedMaxTopStateCount;
- }
-
- public float getEstimatedMaxUtilization() {
- return _estimatedMaxUtilization;
- }
-
- public Set<String> getPartitionsForResourceAndFaultZone(String resourceName, String faultZoneId) {
- return _assignmentForFaultZoneMap.getOrDefault(faultZoneId, Collections.emptyMap())
- .getOrDefault(resourceName, Collections.emptySet());
- }
-
- void addPartitionToFaultZone(String faultZoneId, String resourceName, String partition) {
- if (!_assignmentForFaultZoneMap.computeIfAbsent(faultZoneId, k -> new HashMap<>())
- .computeIfAbsent(resourceName, k -> new HashSet<>())
- .add(partition)) {
- throw new HelixException(
- String.format("Resource %s already has a replica from partition %s in fault zone %s", resourceName, partition,
- faultZoneId));
- }
- }
-
- boolean removePartitionFromFaultZone(String faultZoneId, String resourceName, String partition) {
- return _assignmentForFaultZoneMap.getOrDefault(faultZoneId, Collections.emptyMap())
- .getOrDefault(resourceName, Collections.emptySet())
- .remove(partition);
- }
-
- void setAssignmentForFaultZoneMap(Map<String, Map<String, Set<String>>> assignmentForFaultZoneMap) {
- _assignmentForFaultZoneMap = assignmentForFaultZoneMap;
- }
-
- private int estimateAvgReplicaCount(int replicaCount, int instanceCount) {
- // Use the floor to ensure evenness.
- // Note if we calculate estimation based on ceil, we might have some low usage participants.
- // For example, if the evaluation is between 1 and 2. While we use 2, many participants will be
- // allocated with 2 partitions. And the other participants only has 0 partitions. Otherwise,
- // if we use 1, most participant will have 1 partition assigned and several participant has 2
- // partitions. The later scenario is what we want to achieve.
- return (int) Math.floor((float) replicaCount / instanceCount);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterModel.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterModel.java
deleted file mode 100644
index 57ffa42..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterModel.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.model;
-
-/*
- * 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.
- */
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apache.helix.HelixException;
-
-/**
- * This class wraps the required input for the rebalance algorithm.
- */
-public class ClusterModel {
- private final ClusterContext _clusterContext;
- // Map to track all the assignable replications. <Resource Name, Set<Replicas>>
- private final Map<String, Set<AssignableReplica>> _assignableReplicaMap;
- // The index to find the replication information with a certain state. <Resource, <Key(resource_partition_state), Replica>>
- // Note that the identical replicas are deduped in the index.
- private final Map<String, Map<String, AssignableReplica>> _assignableReplicaIndex;
- private final Map<String, AssignableNode> _assignableNodeMap;
-
- /**
- * @param clusterContext The initialized cluster context.
- * @param assignableReplicas The replicas to be assigned.
- * Note that the replicas in this list shall not be included while initializing the context and assignable nodes.
- * @param assignableNodes The active instances.
- */
- ClusterModel(ClusterContext clusterContext, Set<AssignableReplica> assignableReplicas,
- Set<AssignableNode> assignableNodes) {
- _clusterContext = clusterContext;
-
- // Save all the to be assigned replication
- _assignableReplicaMap = assignableReplicas.stream()
- .collect(Collectors.groupingBy(AssignableReplica::getResourceName, Collectors.toSet()));
-
- // Index all the replicas to be assigned. Dedup the replica if two instances have the same resource/partition/state
- _assignableReplicaIndex = assignableReplicas.stream().collect(Collectors
- .groupingBy(AssignableReplica::getResourceName, Collectors
- .toMap(AssignableReplica::toString, replica -> replica,
- (oldValue, newValue) -> oldValue)));
-
- _assignableNodeMap = assignableNodes.parallelStream()
- .collect(Collectors.toMap(AssignableNode::getInstanceName, node -> node));
- }
-
- public ClusterContext getContext() {
- return _clusterContext;
- }
-
- public Map<String, AssignableNode> getAssignableNodes() {
- return _assignableNodeMap;
- }
-
- public Map<String, Set<AssignableReplica>> getAssignableReplicaMap() {
- return _assignableReplicaMap;
- }
-
- /**
- * Assign the given replica to the specified instance and record the assignment in the cluster model.
- * The cluster usage information will be updated accordingly.
- *
- * @param resourceName
- * @param partitionName
- * @param state
- * @param instanceName
- */
- public void assign(String resourceName, String partitionName, String state, String instanceName) {
- AssignableNode node = locateAssignableNode(instanceName);
- AssignableReplica replica = locateAssignableReplica(resourceName, partitionName, state);
-
- node.assign(replica);
- _clusterContext.addPartitionToFaultZone(node.getFaultZone(), resourceName, partitionName);
- }
-
- /**
- * Revert the proposed assignment from the cluster model.
- * The cluster usage information will be updated accordingly.
- *
- * @param resourceName
- * @param partitionName
- * @param state
- * @param instanceName
- */
- public void release(String resourceName, String partitionName, String state,
- String instanceName) {
- AssignableNode node = locateAssignableNode(instanceName);
- AssignableReplica replica = locateAssignableReplica(resourceName, partitionName, state);
-
- node.release(replica);
- _clusterContext.removePartitionFromFaultZone(node.getFaultZone(), resourceName, partitionName);
- }
-
- private AssignableNode locateAssignableNode(String instanceName) {
- AssignableNode node = _assignableNodeMap.get(instanceName);
- if (node == null) {
- throw new HelixException("Cannot find the instance: " + instanceName);
- }
- return node;
- }
-
- private AssignableReplica locateAssignableReplica(String resourceName, String partitionName,
- String state) {
- AssignableReplica sampleReplica =
- _assignableReplicaIndex.getOrDefault(resourceName, Collections.emptyMap())
- .get(AssignableReplica.generateReplicaKey(resourceName, partitionName, state));
- if (sampleReplica == null) {
- throw new HelixException(String
- .format("Cannot find the replication with resource name %s, partition name %s, state %s.",
- resourceName, partitionName, state));
- }
- return sampleReplica;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterModelProvider.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterModelProvider.java
deleted file mode 100644
index 41c43d6..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/ClusterModelProvider.java
+++ /dev/null
@@ -1,532 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.model;
-
-/*
- * 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.
- */
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apache.helix.HelixConstants;
-import org.apache.helix.HelixException;
-import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
-import org.apache.helix.model.ClusterConfig;
-import org.apache.helix.model.IdealState;
-import org.apache.helix.model.InstanceConfig;
-import org.apache.helix.model.Resource;
-import org.apache.helix.model.ResourceAssignment;
-import org.apache.helix.model.ResourceConfig;
-import org.apache.helix.model.StateModelDefinition;
-
-/**
- * This util class generates Cluster Model object based on the controller's data cache.
- */
-public class ClusterModelProvider {
-
- private enum RebalanceScopeType {
- // Set the rebalance scope to cover the difference between the current assignment and the
- // Baseline assignment only.
- PARTIAL,
- // Set the rebalance scope to cover all replicas that need relocation based on the cluster
- // changes.
- GLOBAL_BASELINE
- }
-
- /**
- * Generate a new Cluster Model object according to the current cluster status for partial
- * rebalance. The rebalance scope is configured for recovering the missing replicas that are in
- * the Baseline assignment but not in the current Best possible assignment only.
- * @param dataProvider The controller's data cache.
- * @param resourceMap The full list of the resources to be rebalanced. Note that any
- * resources that are not in this list will be removed from the
- * final assignment.
- * @param activeInstances The active instances that will be used in the calculation.
- * Note this list can be different from the real active node list
- * according to the rebalancer logic.
- * @param baselineAssignment The persisted Baseline assignment.
- * @param bestPossibleAssignment The persisted Best Possible assignment that was generated in the
- * previous rebalance.
- * @return
- */
- public static ClusterModel generateClusterModelForPartialRebalance(
- ResourceControllerDataProvider dataProvider, Map<String, Resource> resourceMap,
- Set<String> activeInstances, Map<String, ResourceAssignment> baselineAssignment,
- Map<String, ResourceAssignment> bestPossibleAssignment) {
- return generateClusterModel(dataProvider, resourceMap, activeInstances, Collections.emptyMap(),
- baselineAssignment, bestPossibleAssignment, RebalanceScopeType.PARTIAL);
- }
-
- /**
- * Generate a new Cluster Model object according to the current cluster status for the Baseline
- * calculation. The rebalance scope is determined according to the cluster changes.
- * @param dataProvider The controller's data cache.
- * @param resourceMap The full list of the resources to be rebalanced. Note that any
- * resources that are not in this list will be removed from the
- * final assignment.
- * @param allInstances All the instances that will be used in the calculation.
- * @param clusterChanges All the cluster changes that happened after the previous rebalance.
- * @param baselineAssignment The previous Baseline assignment.
- * @return the new cluster model
- */
- public static ClusterModel generateClusterModelForBaseline(
- ResourceControllerDataProvider dataProvider, Map<String, Resource> resourceMap,
- Set<String> allInstances, Map<HelixConstants.ChangeType, Set<String>> clusterChanges,
- Map<String, ResourceAssignment> baselineAssignment) {
- return generateClusterModel(dataProvider, resourceMap, allInstances, clusterChanges,
- Collections.emptyMap(), baselineAssignment, RebalanceScopeType.GLOBAL_BASELINE);
- }
-
- /**
- * Generate a cluster model based on the current state output and data cache. The rebalance scope
- * is configured for recovering the missing replicas only.
- * @param dataProvider The controller's data cache.
- * @param resourceMap The full list of the resources to be rebalanced. Note that any
- * resources that are not in this list will be removed from the
- * final assignment.
- * @param currentStateAssignment The resource assignment built from current state output.
- * @return the new cluster model
- */
- public static ClusterModel generateClusterModelFromExistingAssignment(
- ResourceControllerDataProvider dataProvider, Map<String, Resource> resourceMap,
- Map<String, ResourceAssignment> currentStateAssignment) {
- return generateClusterModel(dataProvider, resourceMap, dataProvider.getEnabledLiveInstances(),
- Collections.emptyMap(), Collections.emptyMap(), currentStateAssignment,
- RebalanceScopeType.GLOBAL_BASELINE);
- }
-
- /**
- * Generate a new Cluster Model object according to the current cluster status.
- * @param dataProvider The controller's data cache.
- * @param resourceMap The full list of the resources to be rebalanced. Note that any
- * resources that are not in this list will be removed from the
- * final assignment.
- * @param activeInstances The active instances that will be used in the calculation.
- * Note this list can be different from the real active node list
- * according to the rebalancer logic.
- * @param clusterChanges All the cluster changes that happened after the previous rebalance.
- * @param idealAssignment The ideal assignment.
- * @param currentAssignment The current assignment that was generated in the previous rebalance.
- * @param scopeType Specify how to determine the rebalance scope.
- * @return the new cluster model
- */
- private static ClusterModel generateClusterModel(ResourceControllerDataProvider dataProvider,
- Map<String, Resource> resourceMap, Set<String> activeInstances,
- Map<HelixConstants.ChangeType, Set<String>> clusterChanges,
- Map<String, ResourceAssignment> idealAssignment,
- Map<String, ResourceAssignment> currentAssignment, RebalanceScopeType scopeType) {
- // Construct all the assignable nodes and initialize with the allocated replicas.
- Set<AssignableNode> assignableNodes =
- getAllAssignableNodes(dataProvider.getClusterConfig(), dataProvider.getInstanceConfigMap(),
- activeInstances);
-
- // Generate replica objects for all the resource partitions.
- // <resource, replica set>
- Map<String, Set<AssignableReplica>> replicaMap =
- getAllAssignableReplicas(dataProvider, resourceMap, assignableNodes);
-
- // Check if the replicas need to be reassigned.
- Map<String, Set<AssignableReplica>> allocatedReplicas =
- new HashMap<>(); // <instanceName, replica set>
- Set<AssignableReplica> toBeAssignedReplicas;
- switch (scopeType) {
- case GLOBAL_BASELINE:
- toBeAssignedReplicas = findToBeAssignedReplicasByClusterChanges(replicaMap, activeInstances,
- dataProvider.getLiveInstances().keySet(), clusterChanges, currentAssignment,
- allocatedReplicas);
- break;
- case PARTIAL:
- // Filter to remove the replicas that do not exist in the ideal assignment given but exist
- // in the replicaMap. This is because such replicas are new additions that do not need to be
- // rebalanced right away.
- retainExistingReplicas(replicaMap, idealAssignment);
- toBeAssignedReplicas =
- findToBeAssignedReplicasByComparingWithIdealAssignment(replicaMap, activeInstances,
- idealAssignment, currentAssignment, allocatedReplicas);
- break;
- default:
- throw new HelixException("Unknown rebalance scope type: " + scopeType);
- }
-
- // Update the allocated replicas to the assignable nodes.
- assignableNodes.parallelStream().forEach(node -> node.assignInitBatch(
- allocatedReplicas.getOrDefault(node.getInstanceName(), Collections.emptySet())));
-
- // Construct and initialize cluster context.
- ClusterContext context = new ClusterContext(
- replicaMap.values().stream().flatMap(Set::stream).collect(Collectors.toSet()),
- assignableNodes, idealAssignment, currentAssignment);
- // Initial the cluster context with the allocated assignments.
- context.setAssignmentForFaultZoneMap(mapAssignmentToFaultZone(assignableNodes));
-
- return new ClusterModel(context, toBeAssignedReplicas, assignableNodes);
- }
-
- // Filter the replicas map so only the replicas that have been allocated in the existing
- // assignmentMap remain in the map.
- private static void retainExistingReplicas(Map<String, Set<AssignableReplica>> replicaMap,
- Map<String, ResourceAssignment> assignmentMap) {
- replicaMap.entrySet().parallelStream().forEach(replicaSetEntry -> {
- // <partition, <state, instances set>>
- Map<String, Map<String, Set<String>>> stateInstanceMap =
- getStateInstanceMap(assignmentMap.get(replicaSetEntry.getKey()));
- // Iterate the replicas of the resource to find the ones that require reallocating.
- Iterator<AssignableReplica> replicaIter = replicaSetEntry.getValue().iterator();
- while (replicaIter.hasNext()) {
- AssignableReplica replica = replicaIter.next();
- Set<String> validInstances =
- stateInstanceMap.getOrDefault(replica.getPartitionName(), Collections.emptyMap())
- .getOrDefault(replica.getReplicaState(), Collections.emptySet());
- if (validInstances.isEmpty()) {
- // Removing the replica if it is not known in the assignment map.
- replicaIter.remove();
- } else {
- // Remove the instance from the state map record after processing so it won't be
- // double-processed as we loop through all replica
- validInstances.remove(validInstances.iterator().next());
- }
- }
- });
- }
-
- /**
- * Find the minimum set of replicas that need to be reassigned by comparing the current assignment
- * with the ideal assignment.
- * A replica needs to be reassigned or newly assigned if either of the following conditions is true:
- * 1. The partition allocation (the instance the replica is placed on) in the ideal assignment and
- * the current assignment are different. And the allocation in the ideal assignment is valid.
- * So it is worthwhile to move it.
- * 2. The partition allocation is in neither the ideal assignment nor the current assignment. Or
- * those allocations are not valid due to offline or disabled instances.
- * Otherwise, the rebalancer just keeps the current assignment allocation.
- *
- * @param replicaMap A map contains all the replicas grouped by resource name.
- * @param activeInstances All the instances that are live and enabled according to the delay rebalance configuration.
- * @param idealAssignment The ideal assignment.
- * @param currentAssignment The current assignment that was generated in the previous rebalance.
- * @param allocatedReplicas A map of <Instance -> replicas> to return the allocated replicas grouped by the target instance name.
- * @return The replicas that need to be reassigned.
- */
- private static Set<AssignableReplica> findToBeAssignedReplicasByComparingWithIdealAssignment(
- Map<String, Set<AssignableReplica>> replicaMap, Set<String> activeInstances,
- Map<String, ResourceAssignment> idealAssignment,
- Map<String, ResourceAssignment> currentAssignment,
- Map<String, Set<AssignableReplica>> allocatedReplicas) {
- Set<AssignableReplica> toBeAssignedReplicas = new HashSet<>();
- // check each resource to identify the allocated replicas and to-be-assigned replicas.
- for (String resourceName : replicaMap.keySet()) {
- // <partition, <state, instances set>>
- Map<String, Map<String, Set<String>>> idealPartitionStateMap =
- getValidStateInstanceMap(idealAssignment.get(resourceName), activeInstances);
- Map<String, Map<String, Set<String>>> currentPartitionStateMap =
- getValidStateInstanceMap(currentAssignment.get(resourceName), activeInstances);
- // Iterate the replicas of the resource to find the ones that require reallocating.
- for (AssignableReplica replica : replicaMap.get(resourceName)) {
- String partitionName = replica.getPartitionName();
- String replicaState = replica.getReplicaState();
- Set<String> idealAllocations =
- idealPartitionStateMap.getOrDefault(partitionName, Collections.emptyMap())
- .getOrDefault(replicaState, Collections.emptySet());
- Set<String> currentAllocations =
- currentPartitionStateMap.getOrDefault(partitionName, Collections.emptyMap())
- .getOrDefault(replicaState, Collections.emptySet());
-
- // Compare the current assignments with the ideal assignment for the common part.
- List<String> commonAllocations = new ArrayList<>(currentAllocations);
- commonAllocations.retainAll(idealAllocations);
- if (!commonAllocations.isEmpty()) {
- // 1. If the partition is allocated at the same location in both ideal and current
- // assignments, there is no need to reassign it.
- String allocatedInstance = commonAllocations.get(0);
- allocatedReplicas.computeIfAbsent(allocatedInstance, key -> new HashSet<>()).add(replica);
- // Remove the instance from the record to prevent this instance from being processed twice.
- idealAllocations.remove(allocatedInstance);
- currentAllocations.remove(allocatedInstance);
- } else if (!idealAllocations.isEmpty()) {
- // 2. If the partition is allocated at an active instance in the ideal assignment but the
- // same allocation does not exist in the current assignment, try to rebalance the replica
- // or assign it if the replica has not been assigned.
- // There are two possible conditions,
- // * This replica has been newly added and has not been assigned yet, so it appears in
- // the ideal assignment and does not appear in the current assignment.
- // * The allocation of this replica in the ideal assignment has been updated due to a
- // cluster change. For example, new instance is added. So the old allocation in the
- // current assignment might be sub-optimal.
- // In either condition, we add it to toBeAssignedReplicas so that it will get assigned.
- toBeAssignedReplicas.add(replica);
- // Remove the pending allocation from the idealAllocations after processing so that the
- // instance won't be double-processed as we loop through all replicas
- String pendingAllocation = idealAllocations.iterator().next();
- idealAllocations.remove(pendingAllocation);
- } else if (!currentAllocations.isEmpty()) {
- // 3. This replica exists in the current assignment but does not appear or has a valid
- // allocation in the ideal assignment.
- // This means either 1) that the ideal assignment actually has this replica allocated on
- // this instance, but it does not show up because the instance is temporarily offline or
- // disabled (note that all such instances have been filtered out in earlier part of the
- // logic) or that the most recent version of the ideal assignment was not fetched
- // correctly from the assignment metadata store.
- // In either case, the solution is to keep the current assignment. So put this replica
- // with the allocated instance into the allocatedReplicas map.
- String allocatedInstance = currentAllocations.iterator().next();
- allocatedReplicas.computeIfAbsent(allocatedInstance, key -> new HashSet<>()).add(replica);
- // Remove the instance from the record to prevent the same location being processed again.
- currentAllocations.remove(allocatedInstance);
- } else {
- // 4. This replica is not found in either the ideal assignment or the current assignment
- // with a valid allocation. This implies that the replica was newly added but was never
- // assigned in reality or was added so recently that it hasn't shown up in the ideal
- // assignment (because it's calculation takes longer and is asynchronously calculated).
- // In that case, we add it to toBeAssignedReplicas so that it will get assigned as a
- // result of partialRebalance.
- toBeAssignedReplicas.add(replica);
- }
- }
- }
- return toBeAssignedReplicas;
- }
-
- /**
- * Find the minimum set of replicas that need to be reassigned according to the cluster change.
- * A replica needs to be reassigned if one of the following condition is true:
- * 1. Cluster topology (the cluster config / any instance config) has been updated.
- * 2. The resource config has been updated.
- * 3. If the current assignment does not contain the partition's valid assignment.
- *
- * @param replicaMap A map contains all the replicas grouped by resource name.
- * @param activeInstances All the instances that are live and enabled according to the delay rebalance configuration.
- * @param liveInstances All the instances that are live.
- * @param clusterChanges A map that contains all the important metadata updates that happened after the previous rebalance.
- * @param currentAssignment The current replica assignment.
- * @param allocatedReplicas Return the allocated replicas grouped by the target instance name.
- * @return The replicas that need to be reassigned.
- */
- private static Set<AssignableReplica> findToBeAssignedReplicasByClusterChanges(
- Map<String, Set<AssignableReplica>> replicaMap, Set<String> activeInstances,
- Set<String> liveInstances, Map<HelixConstants.ChangeType, Set<String>> clusterChanges,
- Map<String, ResourceAssignment> currentAssignment,
- Map<String, Set<AssignableReplica>> allocatedReplicas) {
- Set<AssignableReplica> toBeAssignedReplicas = new HashSet<>();
-
- // A newly connected node = A new LiveInstance znode (or session Id updated) & the
- // corresponding instance is live.
- // TODO: The assumption here is that if the LiveInstance znode is created or it's session Id is
- // TODO: updated, we need to call algorithm for moving some partitions to this new node.
- // TODO: However, if the liveInstance znode is changed because of some other reason, it will be
- // TODO: treated as a newly connected nodes. We need to find a better way to identify which one
- // TODO: is the real newly connected nodes.
- Set<String> newlyConnectedNodes = clusterChanges
- .getOrDefault(HelixConstants.ChangeType.LIVE_INSTANCE, Collections.emptySet());
- newlyConnectedNodes.retainAll(liveInstances);
- if (clusterChanges.containsKey(HelixConstants.ChangeType.CLUSTER_CONFIG) || clusterChanges
- .containsKey(HelixConstants.ChangeType.INSTANCE_CONFIG) || !newlyConnectedNodes.isEmpty()) {
- // 1. If the cluster topology has been modified, need to reassign all replicas.
- // 2. If any node was newly connected, need to rebalance all replicas for the evenness of
- // distribution.
- toBeAssignedReplicas
- .addAll(replicaMap.values().stream().flatMap(Set::stream).collect(Collectors.toSet()));
- } else {
- // check each resource to identify the allocated replicas and to-be-assigned replicas.
- for (Map.Entry<String, Set<AssignableReplica>> replicaMapEntry : replicaMap.entrySet()) {
- String resourceName = replicaMapEntry.getKey();
- Set<AssignableReplica> replicas = replicaMapEntry.getValue();
- // 1. if the resource config/idealstate is changed, need to reassign.
- // 2. if the resource does not appear in the current assignment, need to reassign.
- if (clusterChanges
- .getOrDefault(HelixConstants.ChangeType.RESOURCE_CONFIG, Collections.emptySet())
- .contains(resourceName) || clusterChanges
- .getOrDefault(HelixConstants.ChangeType.IDEAL_STATE, Collections.emptySet())
- .contains(resourceName) || !currentAssignment.containsKey(resourceName)) {
- toBeAssignedReplicas.addAll(replicas);
- continue; // go to check next resource
- } else {
- // check for every replica assignment to identify if the related replicas need to be reassigned.
- // <partition, <state, instances list>>
- Map<String, Map<String, Set<String>>> stateMap =
- getValidStateInstanceMap(currentAssignment.get(resourceName), activeInstances);
- for (AssignableReplica replica : replicas) {
- // Find any ACTIVE instance allocation that has the same state with the replica
- Set<String> validInstances =
- stateMap.getOrDefault(replica.getPartitionName(), Collections.emptyMap())
- .getOrDefault(replica.getReplicaState(), Collections.emptySet());
- if (validInstances.isEmpty()) {
- // 3. if no such an instance in the current assignment, need to reassign the replica
- toBeAssignedReplicas.add(replica);
- continue; // go to check the next replica
- } else {
- Iterator<String> iter = validInstances.iterator();
- // Remove the instance from the current allocation record after processing so that it
- // won't be double-processed as we loop through all replicas
- String instanceName = iter.next();
- iter.remove();
- // the current assignment for this replica is valid,
- // add to the allocated replica list.
- allocatedReplicas.computeIfAbsent(instanceName, key -> new HashSet<>()).add(replica);
- }
- }
- }
- }
- }
- return toBeAssignedReplicas;
- }
-
- /**
- * Filter to remove all invalid allocations that are not on the active instances.
- * @param assignment
- * @param activeInstances
- * @return A map of <partition, <state, instances set>> contains the valid state to instance map.
- */
- private static Map<String, Map<String, Set<String>>> getValidStateInstanceMap(
- ResourceAssignment assignment, Set<String> activeInstances) {
- Map<String, Map<String, Set<String>>> stateInstanceMap = getStateInstanceMap(assignment);
- stateInstanceMap.values().stream().forEach(stateMap -> stateMap.values().stream()
- .forEach(instanceSet -> instanceSet.retainAll(activeInstances)));
- return stateInstanceMap;
- }
-
- // <partition, <state, instances set>>
- private static Map<String, Map<String, Set<String>>> getStateInstanceMap(
- ResourceAssignment assignment) {
- if (assignment == null) {
- return Collections.emptyMap();
- }
- return assignment.getMappedPartitions().stream()
- .collect(Collectors.toMap(partition -> partition.getPartitionName(), partition -> {
- Map<String, Set<String>> stateInstanceMap = new HashMap<>();
- assignment.getReplicaMap(partition).entrySet().stream().forEach(
- stateMapEntry -> stateInstanceMap
- .computeIfAbsent(stateMapEntry.getValue(), key -> new HashSet<>())
- .add(stateMapEntry.getKey()));
- return stateInstanceMap;
- }));
- }
-
- /**
- * Get all the nodes that can be assigned replicas based on the configurations.
- *
- * @param clusterConfig The cluster configuration.
- * @param instanceConfigMap A map of all the instance configuration.
- * If any active instance has no configuration, it will be ignored.
- * @param activeInstances All the instances that are online and enabled.
- * @return A map of assignable node set, <InstanceName, node set>.
- */
- private static Set<AssignableNode> getAllAssignableNodes(ClusterConfig clusterConfig,
- Map<String, InstanceConfig> instanceConfigMap, Set<String> activeInstances) {
- return activeInstances.parallelStream()
- .filter(instance -> instanceConfigMap.containsKey(instance)).map(
- instanceName -> new AssignableNode(clusterConfig, instanceConfigMap.get(instanceName),
- instanceName)).collect(Collectors.toSet());
- }
-
- /**
- * Get all the replicas that need to be reallocated from the cluster data cache.
- *
- * @param dataProvider The cluster status cache that contains the current cluster status.
- * @param resourceMap All the valid resources that are managed by the rebalancer.
- * @param assignableNodes All the active assignable nodes.
- * @return A map of assignable replica set, <ResourceName, replica set>.
- */
- private static Map<String, Set<AssignableReplica>> getAllAssignableReplicas(
- ResourceControllerDataProvider dataProvider, Map<String, Resource> resourceMap,
- Set<AssignableNode> assignableNodes) {
- ClusterConfig clusterConfig = dataProvider.getClusterConfig();
- int activeFaultZoneCount = assignableNodes.stream().map(node -> node.getFaultZone())
- .collect(Collectors.toSet()).size();
- return resourceMap.keySet().parallelStream().map(resourceName -> {
- ResourceConfig resourceConfig = dataProvider.getResourceConfig(resourceName);
- if (resourceConfig == null) {
- resourceConfig = new ResourceConfig(resourceName);
- }
- IdealState is = dataProvider.getIdealState(resourceName);
- if (is == null) {
- throw new HelixException(
- "Cannot find the resource ideal state for resource: " + resourceName);
- }
- String defName = is.getStateModelDefRef();
- StateModelDefinition def = dataProvider.getStateModelDef(defName);
- if (def == null) {
- throw new IllegalArgumentException(String
- .format("Cannot find state model definition %s for resource %s.",
- is.getStateModelDefRef(), resourceName));
- }
- Map<String, Integer> stateCountMap =
- def.getStateCountMap(activeFaultZoneCount, is.getReplicaCount(assignableNodes.size()));
- mergeIdealStateWithResourceConfig(resourceConfig, is);
- Set<AssignableReplica> replicas = new HashSet<>();
- for (String partition : is.getPartitionSet()) {
- for (Map.Entry<String, Integer> entry : stateCountMap.entrySet()) {
- String state = entry.getKey();
- for (int i = 0; i < entry.getValue(); i++) {
- replicas.add(new AssignableReplica(clusterConfig, resourceConfig, partition, state,
- def.getStatePriorityMap().get(state)));
- }
- }
- }
- return new HashMap.SimpleEntry<>(resourceName, replicas);
- }).collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
- }
-
- /**
- * For backward compatibility, propagate the critical simple fields from the IdealState to
- * the Resource Config.
- * Eventually, Resource Config should be the only metadata node that contains the required information.
- */
- private static void mergeIdealStateWithResourceConfig(ResourceConfig resourceConfig,
- final IdealState idealState) {
- // Note that the config fields get updated in this method shall be fully compatible with ones in the IdealState.
- // 1. The fields shall have exactly the same meaning.
- // 2. The value shall be exactly compatible, no additional calculation involved.
- // 3. Resource Config items have a high priority.
- // This is to ensure the resource config is not polluted after the merge.
- if (null == resourceConfig.getRecord()
- .getSimpleField(ResourceConfig.ResourceConfigProperty.INSTANCE_GROUP_TAG.name())) {
- resourceConfig.getRecord()
- .setSimpleField(ResourceConfig.ResourceConfigProperty.INSTANCE_GROUP_TAG.name(),
- idealState.getInstanceGroupTag());
- }
- if (null == resourceConfig.getRecord()
- .getSimpleField(ResourceConfig.ResourceConfigProperty.MAX_PARTITIONS_PER_INSTANCE.name())) {
- resourceConfig.getRecord()
- .setIntField(ResourceConfig.ResourceConfigProperty.MAX_PARTITIONS_PER_INSTANCE.name(),
- idealState.getMaxPartitionsPerInstance());
- }
- }
-
- /**
- * @return A map containing the assignments for each fault zone. <fault zone, <resource, set of partitions>>
- */
- private static Map<String, Map<String, Set<String>>> mapAssignmentToFaultZone(
- Set<AssignableNode> assignableNodes) {
- Map<String, Map<String, Set<String>>> faultZoneAssignmentMap = new HashMap<>();
- assignableNodes.stream().forEach(node -> {
- for (Map.Entry<String, Set<String>> resourceMap : node.getAssignedPartitionsMap()
- .entrySet()) {
- faultZoneAssignmentMap.computeIfAbsent(node.getFaultZone(), k -> new HashMap<>())
- .computeIfAbsent(resourceMap.getKey(), k -> new HashSet<>())
- .addAll(resourceMap.getValue());
- }
- });
- return faultZoneAssignmentMap;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/OptimalAssignment.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/OptimalAssignment.java
deleted file mode 100644
index 1ff00c9..0000000
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/OptimalAssignment.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package org.apache.helix.controller.rebalancer.waged.model;
-
-/*
- * 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.
- */
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.helix.HelixException;
-import org.apache.helix.model.Partition;
-import org.apache.helix.model.ResourceAssignment;
-
-/**
- * The data model represents the optimal assignment of N replicas assigned to M instances;
- * It's mostly used as the return parameter of an assignment calculation algorithm; If the algorithm
- * failed to find optimal assignment given the endeavor, the user could check the failure reasons.
- * Note that this class is not thread safe.
- */
-public class OptimalAssignment {
- private Map<String, ResourceAssignment> _optimalAssignment = Collections.emptyMap();
- private Map<AssignableReplica, Map<AssignableNode, List<String>>> _failedAssignments =
- new HashMap<>();
-
- /**
- * Update the OptimalAssignment instance with the existing assignment recorded in the input cluster model.
- *
- * @param clusterModel
- */
- public void updateAssignments(ClusterModel clusterModel) {
- Map<String, ResourceAssignment> assignmentMap = new HashMap<>();
- for (AssignableNode node : clusterModel.getAssignableNodes().values()) {
- for (AssignableReplica replica : node.getAssignedReplicas()) {
- String resourceName = replica.getResourceName();
- Partition partition = new Partition(replica.getPartitionName());
- ResourceAssignment resourceAssignment = assignmentMap
- .computeIfAbsent(resourceName, key -> new ResourceAssignment(resourceName));
- Map<String, String> partitionStateMap = resourceAssignment.getReplicaMap(partition);
- if (partitionStateMap.isEmpty()) {
- // ResourceAssignment returns immutable empty map while no such assignment recorded yet.
- // So if the returned map is empty, create a new map.
- partitionStateMap = new HashMap<>();
- }
- partitionStateMap.put(node.getInstanceName(), replica.getReplicaState());
- resourceAssignment.addReplicaMap(partition, partitionStateMap);
- }
- }
- _optimalAssignment = assignmentMap;
- }
-
- /**
- * @return The optimal assignment in the form of a <Resource Name, ResourceAssignment> map.
- */
- public Map<String, ResourceAssignment> getOptimalResourceAssignment() {
- if (hasAnyFailure()) {
- throw new HelixException(
- "Cannot get the optimal resource assignment since a calculation failure is recorded. "
- + getFailures());
- }
- return _optimalAssignment;
- }
-
- public void recordAssignmentFailure(AssignableReplica replica,
- Map<AssignableNode, List<String>> failedReasons) {
- _failedAssignments.put(replica, failedReasons);
- }
-
- public boolean hasAnyFailure() {
- return !_failedAssignments.isEmpty();
- }
-
- public String getFailures() {
- // TODO: format the error string
- return _failedAssignments.toString();
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/AttributeName.java b/helix-core/src/main/java/org/apache/helix/controller/stages/AttributeName.java
index b570568..a2b63f8 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/AttributeName.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/AttributeName.java
@@ -38,6 +38,5 @@ public enum AttributeName {
AsyncFIFOWorkerPool,
PipelineType,
LastRebalanceFinishTimeStamp,
- ControllerDataProvider,
- STATEFUL_REBALANCER
+ ControllerDataProvider
}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java b/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
index ffaac8f..49a72e0 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/BestPossibleStateCalcStage.java
@@ -20,17 +20,13 @@ package org.apache.helix.controller.stages;
*/
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
-import java.util.stream.Collectors;
import org.apache.helix.HelixException;
import org.apache.helix.HelixManager;
-import org.apache.helix.HelixRebalanceException;
import org.apache.helix.controller.LogUtil;
import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
import org.apache.helix.controller.pipeline.AbstractBaseStage;
@@ -41,8 +37,6 @@ import org.apache.helix.controller.rebalancer.MaintenanceRebalancer;
import org.apache.helix.controller.rebalancer.Rebalancer;
import org.apache.helix.controller.rebalancer.SemiAutoRebalancer;
import org.apache.helix.controller.rebalancer.internal.MappingCalculator;
-import org.apache.helix.controller.rebalancer.waged.WagedRebalancer;
-import org.apache.helix.model.ClusterConfig;
import org.apache.helix.model.IdealState;
import org.apache.helix.model.InstanceConfig;
import org.apache.helix.model.MaintenanceSignal;
@@ -62,19 +56,18 @@ import org.slf4j.LoggerFactory;
* IdealState,StateModel,LiveInstance
*/
public class BestPossibleStateCalcStage extends AbstractBaseStage {
- private static final Logger logger =
- LoggerFactory.getLogger(BestPossibleStateCalcStage.class.getName());
+ private static final Logger logger = LoggerFactory.getLogger(BestPossibleStateCalcStage.class.getName());
@Override
public void process(ClusterEvent event) throws Exception {
_eventId = event.getEventId();
- CurrentStateOutput currentStateOutput = event.getAttribute(AttributeName.CURRENT_STATE.name());
+ CurrentStateOutput currentStateOutput =
+ event.getAttribute(AttributeName.CURRENT_STATE.name());
final Map<String, Resource> resourceMap =
event.getAttribute(AttributeName.RESOURCES_TO_REBALANCE.name());
final ClusterStatusMonitor clusterStatusMonitor =
event.getAttribute(AttributeName.clusterStatusMonitor.name());
- ResourceControllerDataProvider cache =
- event.getAttribute(AttributeName.ControllerDataProvider.name());
+ ResourceControllerDataProvider cache = event.getAttribute(AttributeName.ControllerDataProvider.name());
if (currentStateOutput == null || resourceMap == null || cache == null) {
throw new StageException(
@@ -97,7 +90,8 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage {
resourceMap, stateModelDefMap);
}
} catch (Exception e) {
- LogUtil.logError(logger, _eventId, "Could not update cluster status metrics!", e);
+ LogUtil
+ .logError(logger, _eventId, "Could not update cluster status metrics!", e);
}
return null;
}
@@ -106,57 +100,43 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage {
private BestPossibleStateOutput compute(ClusterEvent event, Map<String, Resource> resourceMap,
CurrentStateOutput currentStateOutput) {
- ResourceControllerDataProvider cache =
- event.getAttribute(AttributeName.ControllerDataProvider.name());
+ ResourceControllerDataProvider cache = event.getAttribute(AttributeName.ControllerDataProvider.name());
BestPossibleStateOutput output = new BestPossibleStateOutput();
HelixManager helixManager = event.getAttribute(AttributeName.helixmanager.name());
ClusterStatusMonitor clusterStatusMonitor =
event.getAttribute(AttributeName.clusterStatusMonitor.name());
- WagedRebalancer wagedRebalancer = event.getAttribute(AttributeName.STATEFUL_REBALANCER.name());
// Check whether the offline/disabled instance count in the cluster reaches the set limit,
// if yes, pause the rebalancer.
- boolean isValid =
- validateOfflineInstancesLimit(cache, event.getAttribute(AttributeName.helixmanager.name()));
+ boolean isValid = validateOfflineInstancesLimit(cache,
+ (HelixManager) event.getAttribute(AttributeName.helixmanager.name()));
final List<String> failureResources = new ArrayList<>();
-
- Map<String, Resource> calculatedResourceMap =
- computeResourceBestPossibleStateWithWagedRebalancer(wagedRebalancer, cache,
- currentStateOutput, resourceMap, output, failureResources);
-
- Map<String, Resource> remainingResourceMap = new HashMap<>(resourceMap);
- remainingResourceMap.keySet().removeAll(calculatedResourceMap.keySet());
-
- // Fallback to the original single resource rebalancer calculation.
- // This is required because we support mixed cluster that uses both WAGED rebalancer and the
- // older rebalancers.
- Iterator<Resource> itr = remainingResourceMap.values().iterator();
+ Iterator<Resource> itr = resourceMap.values().iterator();
while (itr.hasNext()) {
Resource resource = itr.next();
boolean result = false;
try {
- result = computeSingleResourceBestPossibleState(event, cache, currentStateOutput, resource,
- output);
+ result =
+ computeResourceBestPossibleState(event, cache, currentStateOutput, resource, output);
} catch (HelixException ex) {
- LogUtil.logError(logger, _eventId, String
- .format("Exception when calculating best possible states for %s",
- resource.getResourceName()), ex);
+ LogUtil.logError(logger, _eventId,
+ "Exception when calculating best possible states for " + resource.getResourceName(),
+ ex);
}
if (!result) {
failureResources.add(resource.getResourceName());
- LogUtil.logWarn(logger, _eventId, String
- .format("Failed to calculate best possible states for %s", resource.getResourceName()));
+ LogUtil.logWarn(logger, _eventId,
+ "Failed to calculate best possible states for " + resource.getResourceName());
}
}
// Check and report if resource rebalance has failure
updateRebalanceStatus(!isValid || !failureResources.isEmpty(), failureResources, helixManager,
- cache, clusterStatusMonitor, String
- .format("Failed to calculate best possible states for %d resources.",
- failureResources.size()));
+ cache, clusterStatusMonitor,
+ "Failed to calculate best possible states for " + failureResources.size() + " resources.");
return output;
}
@@ -205,9 +185,8 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage {
if (manager != null) {
if (manager.getHelixDataAccessor()
.getProperty(manager.getHelixDataAccessor().keyBuilder().maintenance()) == null) {
- manager.getClusterManagmentTool()
- .autoEnableMaintenanceMode(manager.getClusterName(), true, errMsg,
- MaintenanceSignal.AutoTriggerReason.MAX_OFFLINE_INSTANCES_EXCEEDED);
+ manager.getClusterManagmentTool().autoEnableMaintenanceMode(manager.getClusterName(),
+ true, errMsg, MaintenanceSignal.AutoTriggerReason.MAX_OFFLINE_INSTANCES_EXCEEDED);
LogUtil.logWarn(logger, _eventId, errMsg);
}
} else {
@@ -220,98 +199,8 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage {
return true;
}
- private void updateWagedRebalancer(WagedRebalancer wagedRebalancer, ClusterConfig clusterConfig) {
- if (clusterConfig != null) {
- // Since the rebalance configuration can be updated at runtime, try to update the rebalancer
- // before calculating.
- wagedRebalancer.updateRebalancePreference(clusterConfig.getGlobalRebalancePreference());
- wagedRebalancer
- .setGlobalRebalanceAsyncMode(clusterConfig.isGlobalRebalanceAsyncModeEnabled());
- }
- }
-
- /**
- * Rebalance with the WAGED rebalancer
- * The rebalancer only calculates the new ideal assignment for all the resources that are
- * configured to use the WAGED rebalancer.
- *
- * @param wagedRebalancer The WAGED rebalancer instance.
- * @param cache Cluster data cache.
- * @param currentStateOutput The current state information.
- * @param resourceMap The complete resource map. The method will filter the map for the compatible resources.
- * @param output The best possible state output.
- * @param failureResources The failure records that will be updated if any resource cannot be computed.
- * @return The map of all the calculated resources.
- */
- private Map<String, Resource> computeResourceBestPossibleStateWithWagedRebalancer(
- WagedRebalancer wagedRebalancer, ResourceControllerDataProvider cache,
- CurrentStateOutput currentStateOutput, Map<String, Resource> resourceMap,
- BestPossibleStateOutput output, List<String> failureResources) {
- if (cache.isMaintenanceModeEnabled()) {
- // The WAGED rebalancer won't be used while maintenance mode is enabled.
- return Collections.emptyMap();
- }
-
- // Find the compatible resources: 1. FULL_AUTO 2. Configured to use the WAGED rebalancer
- Map<String, Resource> wagedRebalancedResourceMap =
- resourceMap.entrySet().stream().filter(resourceEntry -> {
- IdealState is = cache.getIdealState(resourceEntry.getKey());
- return is != null && is.getRebalanceMode().equals(IdealState.RebalanceMode.FULL_AUTO)
- && WagedRebalancer.class.getName().equals(is.getRebalancerClassName());
- }).collect(Collectors.toMap(resourceEntry -> resourceEntry.getKey(),
- resourceEntry -> resourceEntry.getValue()));
-
- Map<String, IdealState> newIdealStates = new HashMap<>();
-
- if (wagedRebalancer != null) {
- updateWagedRebalancer(wagedRebalancer, cache.getClusterConfig());
- try {
- newIdealStates.putAll(wagedRebalancer
- .computeNewIdealStates(cache, wagedRebalancedResourceMap, currentStateOutput));
- } catch (HelixRebalanceException ex) {
- // Note that unlike the legacy rebalancer, the WAGED rebalance won't return partial result.
- // Since it calculates for all the eligible resources globally, a partial result is invalid.
- // TODO propagate the rebalancer failure information to updateRebalanceStatus for monitoring.
- LogUtil.logError(logger, _eventId, String
- .format("Failed to calculate the new Ideal States using the rebalancer %s due to %s",
- wagedRebalancer.getClass().getSimpleName(), ex.getFailureType()), ex);
- }
- } else {
- LogUtil.logError(logger, _eventId,
- "Skip rebalancing using the WAGED rebalancer since it is not configured in the rebalance pipeline.");
- }
-
- Iterator<Resource> itr = wagedRebalancedResourceMap.values().iterator();
- while (itr.hasNext()) {
- Resource resource = itr.next();
- IdealState is = newIdealStates.get(resource.getResourceName());
- // Check if the WAGED rebalancer has calculated the result for this resource or not.
- if (is != null && checkBestPossibleStateCalculation(is)) {
- // The WAGED rebalancer calculates a valid result, record in the output
- updateBestPossibleStateOutput(output, resource, is);
- } else {
- failureResources.add(resource.getResourceName());
- LogUtil.logWarn(logger, _eventId, String
- .format("Failed to calculate best possible states for %s.",
- resource.getResourceName()));
- }
- }
- return wagedRebalancedResourceMap;
- }
-
- private void updateBestPossibleStateOutput(BestPossibleStateOutput output, Resource resource,
- IdealState computedIdealState) {
- output.setPreferenceLists(resource.getResourceName(), computedIdealState.getPreferenceLists());
- for (Partition partition : resource.getPartitions()) {
- Map<String, String> newStateMap =
- computedIdealState.getInstanceStateMap(partition.getPartitionName());
- output.setState(resource.getResourceName(), partition, newStateMap);
- }
- }
-
- private boolean computeSingleResourceBestPossibleState(ClusterEvent event,
- ResourceControllerDataProvider cache, CurrentStateOutput currentStateOutput,
- Resource resource, BestPossibleStateOutput output) {
+ private boolean computeResourceBestPossibleState(ClusterEvent event, ResourceControllerDataProvider cache,
+ CurrentStateOutput currentStateOutput, Resource resource, BestPossibleStateOutput output) {
// for each ideal state
// read the state model def
// for each resource
@@ -340,13 +229,12 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage {
Rebalancer<ResourceControllerDataProvider> rebalancer =
getRebalancer(idealState, resourceName, cache.isMaintenanceModeEnabled());
- MappingCalculator<ResourceControllerDataProvider> mappingCalculator =
- getMappingCalculator(rebalancer, resourceName);
+ MappingCalculator<ResourceControllerDataProvider> mappingCalculator = getMappingCalculator(rebalancer, resourceName);
if (rebalancer == null || mappingCalculator == null) {
- LogUtil.logError(logger, _eventId, "Error computing assignment for resource " + resourceName
- + ". no rebalancer found. rebalancer: " + rebalancer + " mappingCalculator: "
- + mappingCalculator);
+ LogUtil.logError(logger, _eventId,
+ "Error computing assignment for resource " + resourceName + ". no rebalancer found. rebalancer: " + rebalancer
+ + " mappingCalculator: " + mappingCalculator);
}
if (rebalancer != null && mappingCalculator != null) {
@@ -411,9 +299,10 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage {
}
}
- private Rebalancer<ResourceControllerDataProvider> getCustomizedRebalancer(
- String rebalancerClassName, String resourceName) {
+ private Rebalancer<ResourceControllerDataProvider> getRebalancer(IdealState idealState, String resourceName,
+ boolean isMaintenanceModeEnabled) {
Rebalancer<ResourceControllerDataProvider> customizedRebalancer = null;
+ String rebalancerClassName = idealState.getRebalancerClassName();
if (rebalancerClassName != null) {
if (logger.isDebugEnabled()) {
LogUtil.logDebug(logger, _eventId,
@@ -427,19 +316,13 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage {
"Exception while invoking custom rebalancer class:" + rebalancerClassName, e);
}
}
- return customizedRebalancer;
- }
- private Rebalancer<ResourceControllerDataProvider> getRebalancer(IdealState idealState,
- String resourceName, boolean isMaintenanceModeEnabled) {
Rebalancer<ResourceControllerDataProvider> rebalancer = null;
switch (idealState.getRebalanceMode()) {
case FULL_AUTO:
if (isMaintenanceModeEnabled) {
rebalancer = new MaintenanceRebalancer();
} else {
- Rebalancer<ResourceControllerDataProvider> customizedRebalancer =
- getCustomizedRebalancer(idealState.getRebalancerClassName(), resourceName);
if (customizedRebalancer != null) {
rebalancer = customizedRebalancer;
} else {
@@ -455,13 +338,14 @@ public class BestPossibleStateCalcStage extends AbstractBaseStage {
break;
case USER_DEFINED:
case TASK:
- rebalancer = getCustomizedRebalancer(idealState.getRebalancerClassName(), resourceName);
+ rebalancer = customizedRebalancer;
break;
default:
LogUtil.logError(logger, _eventId,
"Fail to find the rebalancer, invalid rebalance mode " + idealState.getRebalanceMode());
break;
}
+
return rebalancer;
}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/CurrentStateComputationStage.java b/helix-core/src/main/java/org/apache/helix/controller/stages/CurrentStateComputationStage.java
index 62fda33..66da8ba 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/CurrentStateComputationStage.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/CurrentStateComputationStage.java
@@ -20,31 +20,19 @@ package org.apache.helix.controller.stages;
*/
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.ExecutorService;
-import java.util.stream.Collectors;
import org.apache.helix.controller.LogUtil;
import org.apache.helix.controller.dataproviders.BaseControllerDataProvider;
-import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
import org.apache.helix.controller.pipeline.AbstractBaseStage;
import org.apache.helix.controller.pipeline.StageException;
-import org.apache.helix.controller.rebalancer.util.ResourceUsageCalculator;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterModel;
-import org.apache.helix.controller.rebalancer.waged.model.ClusterModelProvider;
import org.apache.helix.model.CurrentState;
-import org.apache.helix.model.IdealState;
import org.apache.helix.model.LiveInstance;
import org.apache.helix.model.Message;
import org.apache.helix.model.Message.MessageType;
import org.apache.helix.model.Partition;
import org.apache.helix.model.Resource;
-import org.apache.helix.model.ResourceAssignment;
-import org.apache.helix.model.ResourceConfig;
-import org.apache.helix.monitoring.mbeans.ClusterStatusMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -62,8 +50,6 @@ public class CurrentStateComputationStage extends AbstractBaseStage {
_eventId = event.getEventId();
BaseControllerDataProvider cache = event.getAttribute(AttributeName.ControllerDataProvider.name());
final Map<String, Resource> resourceMap = event.getAttribute(AttributeName.RESOURCES.name());
- final Map<String, Resource> resourceToRebalance =
- event.getAttribute(AttributeName.RESOURCES_TO_REBALANCE.name());
if (cache == null || resourceMap == null) {
throw new StageException("Missing attributes in event:" + event
@@ -88,16 +74,6 @@ public class CurrentStateComputationStage extends AbstractBaseStage {
updateCurrentStates(instance, currentStateMap.values(), currentStateOutput, resourceMap);
}
event.addAttribute(AttributeName.CURRENT_STATE.name(), currentStateOutput);
-
- final ClusterStatusMonitor clusterStatusMonitor =
- event.getAttribute(AttributeName.clusterStatusMonitor.name());
- if (clusterStatusMonitor != null && cache instanceof ResourceControllerDataProvider) {
- final ResourceControllerDataProvider dataProvider = (ResourceControllerDataProvider) cache;
- reportInstanceCapacityMetrics(clusterStatusMonitor, dataProvider, resourceToRebalance,
- currentStateOutput);
- reportResourcePartitionCapacityMetrics(dataProvider.getAsyncTasksThreadPool(),
- clusterStatusMonitor, dataProvider.getResourceConfigMap().values());
- }
}
// update all pending messages to CurrentStateOutput.
@@ -244,55 +220,4 @@ public class CurrentStateComputationStage extends AbstractBaseStage {
currentStateOutput.setCancellationMessage(resourceName, partition, instanceName, message);
}
}
-
- private void reportInstanceCapacityMetrics(ClusterStatusMonitor clusterStatusMonitor,
- ResourceControllerDataProvider dataProvider, Map<String, Resource> resourceMap,
- CurrentStateOutput currentStateOutput) {
- asyncExecute(dataProvider.getAsyncTasksThreadPool(), () -> {
- try {
- // ResourceToRebalance map also has resources from current states.
- // Only use the resources in ideal states to parse all replicas.
- Map<String, IdealState> idealStateMap = dataProvider.getIdealStates();
- Map<String, Resource> resourceToMonitorMap = resourceMap.entrySet().stream()
- .filter(idealStateMap::containsKey)
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
-
- Map<String, ResourceAssignment> currentStateAssignment =
- currentStateOutput.getAssignment(resourceToMonitorMap.keySet());
- ClusterModel clusterModel = ClusterModelProvider.generateClusterModelFromExistingAssignment(
- dataProvider, resourceToMonitorMap, currentStateAssignment);
-
- for (AssignableNode node : clusterModel.getAssignableNodes().values()) {
- String instanceName = node.getInstanceName();
- // There is no new usage adding to this node, so an empty map is passed in.
- double usage = node.getProjectedHighestUtilization(Collections.emptyMap());
- clusterStatusMonitor
- .updateInstanceCapacityStatus(instanceName, usage, node.getMaxCapacity());
- }
- } catch (Exception ex) {
- LOG.error("Failed to report instance capacity metrics. Exception message: {}",
- ex.getMessage());
- }
-
- return null;
- });
- }
-
- private void reportResourcePartitionCapacityMetrics(ExecutorService executorService,
- ClusterStatusMonitor clusterStatusMonitor, Collection<ResourceConfig> resourceConfigs) {
- asyncExecute(executorService, () -> {
- try {
- for (ResourceConfig config : resourceConfigs) {
- Map<String, Integer> averageWeight = ResourceUsageCalculator
- .calculateAveragePartitionWeight(config.getPartitionCapacityMap());
- clusterStatusMonitor.updatePartitionWeight(config.getResourceName(), averageWeight);
- }
- } catch (Exception ex) {
- LOG.error("Failed to report resource partition capacity metrics. Exception message: {}",
- ex.getMessage());
- }
-
- return null;
- });
- }
}
diff --git a/helix-core/src/main/java/org/apache/helix/controller/stages/CurrentStateOutput.java b/helix-core/src/main/java/org/apache/helix/controller/stages/CurrentStateOutput.java
index 752a760..bbbf0fd 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/stages/CurrentStateOutput.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/stages/CurrentStateOutput.java
@@ -28,7 +28,6 @@ import com.google.common.collect.Sets;
import org.apache.helix.model.CurrentState;
import org.apache.helix.model.Message;
import org.apache.helix.model.Partition;
-import org.apache.helix.model.ResourceAssignment;
/**
* The current state includes both current state and pending messages
@@ -429,26 +428,4 @@ public class CurrentStateOutput {
return sb.toString();
}
- /**
- * Get current state assignment for a set of resources.
- * @param resourceSet a set of resources' names
- * @return a map of current state resource assignment, {resourceName: resourceAssignment}
- */
- public Map<String, ResourceAssignment> getAssignment(Set<String> resourceSet) {
- Map<String, ResourceAssignment> currentStateAssignment = new HashMap<>();
- for (String resourceName : resourceSet) {
- Map<Partition, Map<String, String>> currentStateMap =
- getCurrentStateMap(resourceName);
- if (!currentStateMap.isEmpty()) {
- ResourceAssignment newResourceAssignment = new ResourceAssignment(resourceName);
- currentStateMap.entrySet().stream().forEach(currentStateEntry -> {
- newResourceAssignment.addReplicaMap(currentStateEntry.getKey(),
- currentStateEntry.getValue());
- });
- currentStateAssignment.put(resourceName, newResourceAssignment);
- }
- }
-
- return currentStateAssignment;
- }
}
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
index 61e75b3..0a978e5 100644
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
+++ b/helix-core/src/main/java/org/apache/helix/manager/zk/ZKHelixAdmin.java
@@ -57,10 +57,6 @@ import org.apache.helix.ZNRecord;
import org.apache.helix.controller.rebalancer.DelayedAutoRebalancer;
import org.apache.helix.controller.rebalancer.strategy.CrushEdRebalanceStrategy;
import org.apache.helix.controller.rebalancer.strategy.RebalanceStrategy;
-import org.apache.helix.controller.rebalancer.util.WagedValidationUtil;
-import org.apache.helix.controller.rebalancer.waged.WagedRebalancer;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableNode;
-import org.apache.helix.controller.rebalancer.waged.model.AssignableReplica;
import org.apache.helix.manager.zk.client.HelixZkClient;
import org.apache.helix.manager.zk.client.SharedZkClientFactory;
import org.apache.helix.model.ClusterConfig;
@@ -80,7 +76,6 @@ import org.apache.helix.model.Message;
import org.apache.helix.model.Message.MessageState;
import org.apache.helix.model.Message.MessageType;
import org.apache.helix.model.PauseSignal;
-import org.apache.helix.model.ResourceConfig;
import org.apache.helix.model.StateModelDefinition;
import org.apache.helix.tools.DefaultIdealStateCalculator;
import org.apache.helix.util.HelixUtil;
@@ -89,7 +84,6 @@ import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
public class ZKHelixAdmin implements HelixAdmin {
public static final String CONNECTION_TIMEOUT = "helixAdmin.timeOutInSec";
private static final String MAINTENANCE_ZNODE_ID = "maintenance";
@@ -186,7 +180,7 @@ public class ZKHelixAdmin implements HelixAdmin {
// does not repeatedly write instance history)
logger.warn("Retrying dropping instance {} with exception {}",
instanceConfig.getInstanceName(), e.getCause().getMessage());
- retryCnt++;
+ retryCnt ++;
} else {
logger.error("Failed to drop instance {} (not retryable).",
instanceConfig.getInstanceName(), e.getCause());
@@ -409,8 +403,7 @@ public class ZKHelixAdmin implements HelixAdmin {
HelixDataAccessor accessor =
new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<ZNRecord>(_zkClient));
Builder keyBuilder = accessor.keyBuilder();
- return accessor.getBaseDataAccessor()
- .exists(keyBuilder.maintenance().getPath(), AccessOption.PERSISTENT);
+ return accessor.getBaseDataAccessor().exists(keyBuilder.maintenance().getPath(), AccessOption.PERSISTENT);
}
@Override
@@ -443,16 +436,16 @@ public class ZKHelixAdmin implements HelixAdmin {
* @param customFields
* @param triggeringEntity
*/
- private void processMaintenanceMode(String clusterName, final boolean enabled,
- final String reason, final MaintenanceSignal.AutoTriggerReason internalReason,
- final Map<String, String> customFields,
+ private void processMaintenanceMode(String clusterName, final boolean enabled, final String reason,
+ final MaintenanceSignal.AutoTriggerReason internalReason, final Map<String, String> customFields,
final MaintenanceSignal.TriggeringEntity triggeringEntity) {
HelixDataAccessor accessor =
new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<ZNRecord>(_zkClient));
Builder keyBuilder = accessor.keyBuilder();
logger.info("Cluster {} {} {} maintenance mode for reason {}.", clusterName,
triggeringEntity == MaintenanceSignal.TriggeringEntity.CONTROLLER ? "automatically"
- : "manually", enabled ? "enters" : "exits", reason == null ? "NULL" : reason);
+ : "manually",
+ enabled ? "enters" : "exits", reason == null ? "NULL" : reason);
final long currentTime = System.currentTimeMillis();
if (!enabled) {
// Exit maintenance mode
@@ -466,23 +459,23 @@ public class ZKHelixAdmin implements HelixAdmin {
maintenanceSignal.setTimestamp(currentTime);
maintenanceSignal.setTriggeringEntity(triggeringEntity);
switch (triggeringEntity) {
- case CONTROLLER:
- // autoEnable
- maintenanceSignal.setAutoTriggerReason(internalReason);
- break;
- case USER:
- case UNKNOWN:
- // manuallyEnable
- if (customFields != null && !customFields.isEmpty()) {
- // Enter all custom fields provided by the user
- Map<String, String> simpleFields = maintenanceSignal.getRecord().getSimpleFields();
- for (Map.Entry<String, String> entry : customFields.entrySet()) {
- if (!simpleFields.containsKey(entry.getKey())) {
- simpleFields.put(entry.getKey(), entry.getValue());
- }
+ case CONTROLLER:
+ // autoEnable
+ maintenanceSignal.setAutoTriggerReason(internalReason);
+ break;
+ case USER:
+ case UNKNOWN:
+ // manuallyEnable
+ if (customFields != null && !customFields.isEmpty()) {
+ // Enter all custom fields provided by the user
+ Map<String, String> simpleFields = maintenanceSignal.getRecord().getSimpleFields();
+ for (Map.Entry<String, String> entry : customFields.entrySet()) {
+ if (!simpleFields.containsKey(entry.getKey())) {
+ simpleFields.put(entry.getKey(), entry.getValue());
}
}
- break;
+ }
+ break;
}
if (!accessor.createMaintenance(maintenanceSignal)) {
throw new HelixException("Failed to create maintenance signal!");
@@ -490,17 +483,16 @@ public class ZKHelixAdmin implements HelixAdmin {
}
// Record a MaintenanceSignal history
- if (!accessor.getBaseDataAccessor()
- .update(keyBuilder.controllerLeaderHistory().getPath(), new DataUpdater<ZNRecord>() {
+ if (!accessor.getBaseDataAccessor().update(keyBuilder.controllerLeaderHistory().getPath(),
+ new DataUpdater<ZNRecord>() {
@Override
public ZNRecord update(ZNRecord oldRecord) {
try {
if (oldRecord == null) {
oldRecord = new ZNRecord(PropertyType.HISTORY.toString());
}
- return new ControllerHistory(oldRecord)
- .updateMaintenanceHistory(enabled, reason, currentTime, internalReason,
- customFields, triggeringEntity);
+ return new ControllerHistory(oldRecord).updateMaintenanceHistory(enabled, reason,
+ currentTime, internalReason, customFields, triggeringEntity);
} catch (IOException e) {
logger.error("Failed to update maintenance history! Exception: {}", e);
return oldRecord;
@@ -1249,8 +1241,7 @@ public class ZKHelixAdmin implements HelixAdmin {
setResourceIdealState(clusterName, resourceName, new IdealState(idealStateRecord));
}
- private static byte[] readFile(String filePath)
- throws IOException {
+ private static byte[] readFile(String filePath) throws IOException {
File file = new File(filePath);
int size = (int) file.length();
@@ -1273,8 +1264,7 @@ public class ZKHelixAdmin implements HelixAdmin {
@Override
public void addStateModelDef(String clusterName, String stateModelDefName,
- String stateModelDefFile)
- throws IOException {
+ String stateModelDefFile) throws IOException {
ZNRecord record =
(ZNRecord) (new ZNRecordSerializer().deserialize(readFile(stateModelDefFile)));
if (record == null || record.getId() == null || !record.getId().equals(stateModelDefName)) {
@@ -1297,9 +1287,9 @@ public class ZKHelixAdmin implements HelixAdmin {
baseAccessor.update(path, new DataUpdater<ZNRecord>() {
@Override
public ZNRecord update(ZNRecord currentData) {
- ClusterConstraints constraints =
- currentData == null ? new ClusterConstraints(constraintType)
- : new ClusterConstraints(currentData);
+ ClusterConstraints constraints = currentData == null ?
+ new ClusterConstraints(constraintType) :
+ new ClusterConstraints(currentData);
constraints.addConstraintItem(constraintId, constraintItem);
return constraints.getRecord();
@@ -1505,7 +1495,9 @@ public class ZKHelixAdmin implements HelixAdmin {
+ ", instance config does not exist");
}
- baseAccessor.update(path, new DataUpdater<ZNRecord>() {
+ baseAccessor.update(path, new DataUpdater<ZNRecord>()
+
+ {
@Override
public ZNRecord update(ZNRecord currentData) {
if (currentData == null) {
@@ -1595,212 +1587,4 @@ public class ZKHelixAdmin implements HelixAdmin {
_zkClient.close();
}
}
-
- @Override
- public boolean addResourceWithWeight(String clusterName, IdealState idealState,
- ResourceConfig resourceConfig) {
- // Null checks
- if (clusterName == null || clusterName.isEmpty()) {
- throw new HelixException("Cluster name is null or empty!");
- }
- if (idealState == null || !idealState.isValid()) {
- throw new HelixException("IdealState is null or invalid!");
- }
- if (resourceConfig == null || !resourceConfig.isValid()) {
- // TODO This might be okay because of default weight?
- throw new HelixException("ResourceConfig is null or invalid!");
- }
-
- // Make sure IdealState and ResourceConfig are for the same resource
- if (!idealState.getResourceName().equals(resourceConfig.getResourceName())) {
- throw new HelixException("Resource names in IdealState and ResourceConfig are different!");
- }
-
- // Order in which a resource should be added:
- // 1. Validate the weights in ResourceConfig against ClusterConfig
- // Check that all capacity keys in ClusterConfig are set up in every partition in ResourceConfig field
- if (!validateWeightForResourceConfig(_configAccessor.getClusterConfig(clusterName),
- resourceConfig, idealState)) {
- throw new HelixException(String
- .format("Could not add resource %s with weight! Failed to validate the ResourceConfig!",
- idealState.getResourceName()));
- }
-
- // 2. Add the resourceConfig to ZK
- _configAccessor
- .setResourceConfig(clusterName, resourceConfig.getResourceName(), resourceConfig);
-
- // 3. Add the idealState to ZK
- setResourceIdealState(clusterName, idealState.getResourceName(), idealState);
-
- // 4. rebalance the resource
- rebalance(clusterName, idealState.getResourceName(), Integer.parseInt(idealState.getReplicas()),
- idealState.getResourceName(), idealState.getInstanceGroupTag());
-
- return true;
- }
-
- @Override
- public boolean enableWagedRebalance(String clusterName, List<String> resourceNames) {
- // Null checks
- if (clusterName == null || clusterName.isEmpty()) {
- throw new HelixException("Cluster name is invalid!");
- }
- if (resourceNames == null || resourceNames.isEmpty()) {
- throw new HelixException("Resource name list is invalid!");
- }
-
- HelixDataAccessor accessor =
- new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<>(_zkClient));
- Builder keyBuilder = accessor.keyBuilder();
- List<IdealState> idealStates = accessor.getChildValues(keyBuilder.idealStates());
- List<String> nullIdealStates = new ArrayList<>();
- for (int i = 0; i < idealStates.size(); i++) {
- if (idealStates.get(i) == null) {
- nullIdealStates.add(resourceNames.get(i));
- } else {
- idealStates.get(i).setRebalancerClassName(WagedRebalancer.class.getName());
- idealStates.get(i).setRebalanceMode(RebalanceMode.FULL_AUTO);
- }
- }
- if (!nullIdealStates.isEmpty()) {
- throw new HelixException(
- String.format("Not all IdealStates exist in the cluster: %s", nullIdealStates));
- }
- List<PropertyKey> idealStateKeys = new ArrayList<>();
- idealStates.forEach(
- idealState -> idealStateKeys.add(keyBuilder.idealStates(idealState.getResourceName())));
- boolean[] success = accessor.setChildren(idealStateKeys, idealStates);
- for (boolean s : success) {
- if (!s) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public Map<String, Boolean> validateResourcesForWagedRebalance(String clusterName,
- List<String> resourceNames) {
- // Null checks
- if (clusterName == null || clusterName.isEmpty()) {
- throw new HelixException("Cluster name is invalid!");
- }
- if (resourceNames == null || resourceNames.isEmpty()) {
- throw new HelixException("Resource name list is invalid!");
- }
-
- // Ensure that all instances are valid
- HelixDataAccessor accessor =
- new ZKHelixDataAccessor(clusterName, new ZkBaseDataAccessor<>(_zkClient));
- Builder keyBuilder = accessor.keyBuilder();
- List<String> instances = accessor.getChildNames(keyBuilder.instanceConfigs());
- if (validateInstancesForWagedRebalance(clusterName, instances).containsValue(false)) {
- throw new HelixException(String
- .format("Instance capacities haven't been configured properly for cluster %s",
- clusterName));
- }
-
- Map<String, Boolean> result = new HashMap<>();
- ClusterConfig clusterConfig = _configAccessor.getClusterConfig(clusterName);
- for (String resourceName : resourceNames) {
- IdealState idealState = getResourceIdealState(clusterName, resourceName);
- if (idealState == null || !idealState.isValid()) {
- result.put(resourceName, false);
- continue;
- }
- ResourceConfig resourceConfig = _configAccessor.getResourceConfig(clusterName, resourceName);
- result.put(resourceName,
- validateWeightForResourceConfig(clusterConfig, resourceConfig, idealState));
- }
- return result;
- }
-
- @Override
- public Map<String, Boolean> validateInstancesForWagedRebalance(String clusterName,
- List<String> instanceNames) {
- // Null checks
- if (clusterName == null || clusterName.isEmpty()) {
- throw new HelixException("Cluster name is invalid!");
- }
- if (instanceNames == null || instanceNames.isEmpty()) {
- throw new HelixException("Instance name list is invalid!");
- }
-
- Map<String, Boolean> result = new HashMap<>();
- ClusterConfig clusterConfig = _configAccessor.getClusterConfig(clusterName);
- for (String instanceName : instanceNames) {
- InstanceConfig instanceConfig = _configAccessor.getInstanceConfig(clusterName, instanceName);
- if (instanceConfig == null || !instanceConfig.isValid()) {
- result.put(instanceName, false);
- continue;
- }
- WagedValidationUtil.validateAndGetInstanceCapacity(clusterConfig, instanceConfig);
- result.put(instanceName, true);
- }
-
- return result;
- }
-
- /**
- * Validates ResourceConfig's weight field against the given ClusterConfig.
- * @param clusterConfig
- * @param resourceConfig
- * @param idealState
- * @return true if ResourceConfig has all the required fields. False otherwise.
- */
- private boolean validateWeightForResourceConfig(ClusterConfig clusterConfig,
- ResourceConfig resourceConfig, IdealState idealState) {
- if (resourceConfig == null) {
- if (clusterConfig.getDefaultPartitionWeightMap().isEmpty()) {
- logger.error(
- "ResourceConfig for {} is null, and there are no default weights set in ClusterConfig!",
- idealState.getResourceName());
- return false;
- }
- // If ResourceConfig is null AND the default partition weight map is defined, and the map has all the required keys, we consider this valid since the default weights will be used
- // Need to check the map contains all the required keys
- if (clusterConfig.getDefaultPartitionWeightMap().keySet()
- .containsAll(clusterConfig.getInstanceCapacityKeys())) {
- // Contains all the required keys, so consider it valid since it will use the default weights
- return true;
- }
- logger.error(
- "ResourceConfig for {} is null, and ClusterConfig's default partition weight map doesn't have all the required keys!",
- idealState.getResourceName());
- return false;
- }
-
- // Parse the entire capacityMap from ResourceConfig
- Map<String, Map<String, Integer>> capacityMap;
- try {
- capacityMap = resourceConfig.getPartitionCapacityMap();
- } catch (IOException ex) {
- logger.error("Invalid partition capacity configuration of resource: {}",
- idealState.getResourceName(), ex);
- return false;
- }
-
- Set<String> capacityMapSet = new HashSet<>(capacityMap.keySet());
- boolean hasDefaultCapacity = capacityMapSet.contains(ResourceConfig.DEFAULT_PARTITION_KEY);
- // Remove DEFAULT key
- capacityMapSet.remove(ResourceConfig.DEFAULT_PARTITION_KEY);
-
- // Make sure capacityMap contains all partitions defined in IdealState
- // Here, IdealState has not been rebalanced, so listFields might be null, in which case, we would get an emptyList from getPartitionSet()
- // So check using numPartitions instead
- // This check allows us to fail early on instead of having to loop through all partitions
- if (capacityMapSet.size() != idealState.getNumPartitions() && !hasDefaultCapacity) {
- logger.error(
- "ResourceConfig for {} does not have all partitions defined in PartitionCapacityMap!",
- idealState.getResourceName());
- return false;
- }
-
- // Loop through all partitions and validate
- capacityMap.keySet().forEach(partitionName -> WagedValidationUtil
- .validateAndGetPartitionCapacity(partitionName, resourceConfig, capacityMap,
- clusterConfig));
- return true;
- }
}
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordJacksonSerializer.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordJacksonSerializer.java
deleted file mode 100644
index b375e80..0000000
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZNRecordJacksonSerializer.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.apache.helix.manager.zk;
-
-/*
- * 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.
- */
-
-import java.io.IOException;
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
-import org.apache.helix.HelixException;
-import org.apache.helix.ZNRecord;
-import org.codehaus.jackson.map.ObjectMapper;
-
-/**
- * ZNRecordJacksonSerializer serializes ZNRecord objects into a byte array using Jackson. Note that
- * this serializer doesn't check for the size of the resulting binary.
- */
-public class ZNRecordJacksonSerializer implements ZkSerializer {
- private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
-
- @Override
- public byte[] serialize(Object record) throws ZkMarshallingError {
- if (!(record instanceof ZNRecord)) {
- // null is NOT an instance of any class
- throw new HelixException("Input object is not of type ZNRecord (was " + record + ")");
- }
- ZNRecord znRecord = (ZNRecord) record;
-
- try {
- return OBJECT_MAPPER.writeValueAsBytes(znRecord);
- } catch (IOException e) {
- throw new HelixException(
- String.format("Exception during serialization. ZNRecord id: %s", znRecord.getId()), e);
- }
- }
-
- @Override
- public Object deserialize(byte[] bytes) throws ZkMarshallingError {
- if (bytes == null || bytes.length == 0) {
- // reading a parent/null node
- return null;
- }
-
- ZNRecord record;
- try {
- record = OBJECT_MAPPER.readValue(bytes, ZNRecord.class);
- } catch (IOException e) {
- throw new HelixException("Exception during deserialization!", e);
- }
- return record;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBucketDataAccessor.java b/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBucketDataAccessor.java
deleted file mode 100644
index bc13471..0000000
--- a/helix-core/src/main/java/org/apache/helix/manager/zk/ZkBucketDataAccessor.java
+++ /dev/null
@@ -1,380 +0,0 @@
-package org.apache.helix.manager.zk;
-
-/*
- * 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.
- */
-
-import com.google.common.collect.ImmutableMap;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.TimerTask;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import org.I0Itec.zkclient.DataUpdater;
-import org.I0Itec.zkclient.exception.ZkMarshallingError;
-import org.I0Itec.zkclient.exception.ZkNoNodeException;
-import org.I0Itec.zkclient.serialize.ZkSerializer;
-import org.apache.helix.AccessOption;
-import org.apache.helix.BucketDataAccessor;
-import org.apache.helix.HelixException;
-import org.apache.helix.HelixProperty;
-import org.apache.helix.ZNRecord;
-import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
-import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.util.GZipCompressionUtil;
-import org.codehaus.jackson.map.ObjectMapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ZkBucketDataAccessor implements BucketDataAccessor, AutoCloseable {
- private static final Logger LOG = LoggerFactory.getLogger(ZkBucketDataAccessor.class);
-
- private static final int DEFAULT_BUCKET_SIZE = 50 * 1024; // 50KB
- private static final long DEFAULT_VERSION_TTL = TimeUnit.MINUTES.toMillis(1L); // 1 min
- private static final String BUCKET_SIZE_KEY = "BUCKET_SIZE";
- private static final String DATA_SIZE_KEY = "DATA_SIZE";
- private static final String METADATA_KEY = "METADATA";
- private static final String LAST_SUCCESSFUL_WRITE_KEY = "LAST_SUCCESSFUL_WRITE";
- private static final String LAST_WRITE_KEY = "LAST_WRITE";
- private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
- // Thread pool for deleting stale versions
- private static final ScheduledExecutorService GC_THREAD = Executors.newScheduledThreadPool(1);
-
- private final int _bucketSize;
- private final long _versionTTL;
- private ZkSerializer _zkSerializer;
- private HelixZkClient _zkClient;
- private ZkBaseDataAccessor<byte[]> _zkBaseDataAccessor;
-
- /**
- * Constructor that allows a custom bucket size.
- * @param zkAddr
- * @param bucketSize
- * @param versionTTL in ms
- */
- public ZkBucketDataAccessor(String zkAddr, int bucketSize, long versionTTL) {
- _zkClient = DedicatedZkClientFactory.getInstance()
- .buildZkClient(new HelixZkClient.ZkConnectionConfig(zkAddr));
- _zkClient.setZkSerializer(new ZkSerializer() {
- @Override
- public byte[] serialize(Object data) throws ZkMarshallingError {
- if (data instanceof byte[]) {
- return (byte[]) data;
- }
- throw new HelixException("ZkBucketDataAccesor only supports a byte array as an argument!");
- }
-
- @Override
- public Object deserialize(byte[] data) throws ZkMarshallingError {
- return data;
- }
- });
- _zkBaseDataAccessor = new ZkBaseDataAccessor<>(_zkClient);
- _zkSerializer = new ZNRecordJacksonSerializer();
- _bucketSize = bucketSize;
- _versionTTL = versionTTL;
- }
-
- /**
- * Constructor that uses a default bucket size.
- * @param zkAddr
- */
- public ZkBucketDataAccessor(String zkAddr) {
- this(zkAddr, DEFAULT_BUCKET_SIZE, DEFAULT_VERSION_TTL);
- }
-
- @Override
- public <T extends HelixProperty> boolean compressedBucketWrite(String rootPath, T value)
- throws IOException {
- DataUpdater<byte[]> lastWriteVersionUpdater = dataInZk -> {
- if (dataInZk == null || dataInZk.length == 0) {
- // No last write version exists, so start with 0
- return "0".getBytes();
- }
- // Last write exists, so increment and write it back
- // **String conversion is necessary to make it display in ZK (zooinspector)**
- String lastWriteVersionStr = new String(dataInZk);
- long lastWriteVersion = Long.parseLong(lastWriteVersionStr);
- lastWriteVersion++;
- return String.valueOf(lastWriteVersion).getBytes();
- };
-
- // 1. Increment lastWriteVersion using DataUpdater
- ZkBaseDataAccessor.AccessResult result = _zkBaseDataAccessor.doUpdate(
- rootPath + "/" + LAST_WRITE_KEY, lastWriteVersionUpdater, AccessOption.PERSISTENT);
- if (result._retCode != ZkBaseDataAccessor.RetCode.OK) {
- throw new HelixException(
- String.format("Failed to write the write version at path: %s!", rootPath));
- }
-
- // Successfully reserved a version number
- byte[] binaryVersion = (byte[]) result._updatedValue;
- String versionStr = new String(binaryVersion);
- final long version = Long.parseLong(versionStr);
-
- // 2. Write to the incremented last write version
- String versionedDataPath = rootPath + "/" + versionStr;
-
- // Take the ZNRecord and serialize it (get byte[])
- byte[] serializedRecord = _zkSerializer.serialize(value.getRecord());
- // Compress the byte[]
- byte[] compressedRecord = GZipCompressionUtil.compress(serializedRecord);
- // Compute N - number of buckets
- int numBuckets = (compressedRecord.length + _bucketSize - 1) / _bucketSize;
-
- List<String> paths = new ArrayList<>();
- List<byte[]> buckets = new ArrayList<>();
-
- int ptr = 0;
- int counter = 0;
- while (counter < numBuckets) {
- paths.add(versionedDataPath + "/" + counter);
- if (counter == numBuckets - 1) {
- // Special treatment for the last bucket
- buckets.add(
- Arrays.copyOfRange(compressedRecord, ptr, ptr + compressedRecord.length % _bucketSize));
- } else {
- buckets.add(Arrays.copyOfRange(compressedRecord, ptr, ptr + _bucketSize));
- }
- ptr += _bucketSize;
- counter++;
- }
-
- // 3. Include the metadata in the batch write
- Map<String, String> metadata = ImmutableMap.of(BUCKET_SIZE_KEY, Integer.toString(_bucketSize),
- DATA_SIZE_KEY, Integer.toString(compressedRecord.length));
- byte[] binaryMetadata = OBJECT_MAPPER.writeValueAsBytes(metadata);
- paths.add(versionedDataPath + "/" + METADATA_KEY);
- buckets.add(binaryMetadata);
-
- // Do an async set to ZK
- boolean[] success = _zkBaseDataAccessor.setChildren(paths, buckets, AccessOption.PERSISTENT);
- // Exception and fail the write if any failed
- for (boolean s : success) {
- if (!s) {
- throw new HelixException(
- String.format("Failed to write the data buckets for path: %s", rootPath));
- }
- }
-
- // 4. Update lastSuccessfulWriteVersion using Updater
- DataUpdater<byte[]> lastSuccessfulWriteVersionUpdater = dataInZk -> {
- if (dataInZk == null || dataInZk.length == 0) {
- // No last write version exists, so write version from this write
- return versionStr.getBytes();
- }
- // Last successful write exists so check if it's smaller than my number
- String lastWriteVersionStr = new String(dataInZk);
- long lastWriteVersion = Long.parseLong(lastWriteVersionStr);
- if (lastWriteVersion < version) {
- // Smaller, so I can overwrite
- return versionStr.getBytes();
- } else {
- // Greater, I have lagged behind. Return null and do not write
- return null;
- }
- };
- if (!_zkBaseDataAccessor.update(rootPath + "/" + LAST_SUCCESSFUL_WRITE_KEY,
- lastSuccessfulWriteVersionUpdater, AccessOption.PERSISTENT)) {
- throw new HelixException(String
- .format("Failed to write the last successful write metadata at path: %s!", rootPath));
- }
-
- // 5. Update the timer for GC
- updateGCTimer(rootPath, versionStr);
- return true;
- }
-
- @Override
- public <T extends HelixProperty> HelixProperty compressedBucketRead(String path,
- Class<T> helixPropertySubType) {
- return helixPropertySubType.cast(compressedBucketRead(path));
- }
-
- @Override
- public void compressedBucketDelete(String path) {
- if (!_zkBaseDataAccessor.remove(path, AccessOption.PERSISTENT)) {
- throw new HelixException(String.format("Failed to delete the bucket data! Path: %s", path));
- }
- }
-
- @Override
- public void disconnect() {
- if (!_zkClient.isClosed()) {
- _zkClient.close();
- }
- }
-
- private HelixProperty compressedBucketRead(String path) {
- // 1. Get the version to read
- byte[] binaryVersionToRead = _zkBaseDataAccessor.get(path + "/" + LAST_SUCCESSFUL_WRITE_KEY,
- null, AccessOption.PERSISTENT);
- if (binaryVersionToRead == null) {
- throw new ZkNoNodeException(
- String.format("Last successful write ZNode does not exist for path: %s", path));
- }
- String versionToRead = new String(binaryVersionToRead);
-
- // 2. Get the metadata map
- byte[] binaryMetadata = _zkBaseDataAccessor.get(path + "/" + versionToRead + "/" + METADATA_KEY,
- null, AccessOption.PERSISTENT);
- if (binaryMetadata == null) {
- throw new ZkNoNodeException(
- String.format("Metadata ZNode does not exist for path: %s", path));
- }
- Map metadata;
- try {
- metadata = OBJECT_MAPPER.readValue(binaryMetadata, Map.class);
- } catch (IOException e) {
- throw new HelixException(String.format("Failed to deserialize path metadata: %s!", path), e);
- }
-
- // 3. Read the data
- Object bucketSizeObj = metadata.get(BUCKET_SIZE_KEY);
- Object dataSizeObj = metadata.get(DATA_SIZE_KEY);
- if (bucketSizeObj == null) {
- throw new HelixException(
- String.format("Metadata ZNRecord does not have %s! Path: %s", BUCKET_SIZE_KEY, path));
- }
- if (dataSizeObj == null) {
- throw new HelixException(
- String.format("Metadata ZNRecord does not have %s! Path: %s", DATA_SIZE_KEY, path));
- }
- int bucketSize = Integer.parseInt((String) bucketSizeObj);
- int dataSize = Integer.parseInt((String) dataSizeObj);
-
- // Compute N - number of buckets
- int numBuckets = (dataSize + _bucketSize - 1) / _bucketSize;
- byte[] compressedRecord = new byte[dataSize];
- String dataPath = path + "/" + versionToRead;
-
- List<String> paths = new ArrayList<>();
- for (int i = 0; i < numBuckets; i++) {
- paths.add(dataPath + "/" + i);
- }
-
- // Async get
- List<byte[]> buckets = _zkBaseDataAccessor.get(paths, null, AccessOption.PERSISTENT, true);
-
- // Combine buckets into one byte array
- int copyPtr = 0;
- for (int i = 0; i < numBuckets; i++) {
- if (i == numBuckets - 1) {
- // Special treatment for the last bucket
- System.arraycopy(buckets.get(i), 0, compressedRecord, copyPtr, dataSize % bucketSize);
- } else {
- System.arraycopy(buckets.get(i), 0, compressedRecord, copyPtr, bucketSize);
- copyPtr += bucketSize;
- }
- }
-
- // Decompress the byte array
- ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedRecord);
- byte[] serializedRecord;
- try {
- serializedRecord = GZipCompressionUtil.uncompress(byteArrayInputStream);
- } catch (IOException e) {
- throw new HelixException(String.format("Failed to decompress path: %s!", path), e);
- }
-
- // Deserialize the record to retrieve the original
- ZNRecord originalRecord = (ZNRecord) _zkSerializer.deserialize(serializedRecord);
- return new HelixProperty(originalRecord);
- }
-
- @Override
- public void close() {
- disconnect();
- }
-
- private void updateGCTimer(String rootPath, String currentVersion) {
- TimerTask gcTask = new TimerTask() {
- @Override
- public void run() {
- deleteStaleVersions(rootPath, currentVersion);
- }
- };
-
- // Schedule the gc task with TTL
- GC_THREAD.schedule(gcTask, _versionTTL, TimeUnit.MILLISECONDS);
- }
-
- /**
- * Deletes all stale versions.
- * @param rootPath
- * @param currentVersion
- */
- private void deleteStaleVersions(String rootPath, String currentVersion) {
- // Get all children names under path
- List<String> children = _zkBaseDataAccessor.getChildNames(rootPath, AccessOption.PERSISTENT);
- if (children == null || children.isEmpty()) {
- // The whole path has been deleted so return immediately
- return;
- }
- filterChildrenNames(children, currentVersion);
- List<String> pathsToDelete = getPathsToDelete(rootPath, children);
- for (String pathToDelete : pathsToDelete) {
- // TODO: Should be batch delete but it doesn't work. It's okay since this runs async
- _zkBaseDataAccessor.remove(pathToDelete, AccessOption.PERSISTENT);
- }
- }
-
- /**
- * Filter out non-version children names and non-stale versions.
- * @param children
- */
- private void filterChildrenNames(List<String> children, String currentVersion) {
- // Leave out metadata
- children.remove(LAST_SUCCESSFUL_WRITE_KEY);
- children.remove(LAST_WRITE_KEY);
-
- // Leave out currentVersion and above
- // This is because we want to honor the TTL for newer versions
- children.remove(currentVersion);
- long currentVer = Long.parseLong(currentVersion);
- for (String child : children) {
- try {
- long version = Long.parseLong(child);
- if (version >= currentVer) {
- children.remove(child);
- }
- } catch (Exception e) {
- // Ignore ZNode names that aren't parseable
- children.remove(child);
- LOG.debug("Found an invalid ZNode: {}", child);
- }
- }
- }
-
- /**
- * Generates all stale paths to delete.
- * @param path
- * @param staleVersions
- * @return
- */
- private List<String> getPathsToDelete(String path, List<String> staleVersions) {
- List<String> pathsToDelete = new ArrayList<>();
- staleVersions.forEach(ver -> pathsToDelete.add(path + "/" + ver));
- return pathsToDelete;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java b/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java
index f88d2f5..bb478c3 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java
@@ -24,9 +24,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.apache.helix.HelixException;
import org.apache.helix.HelixProperty;
@@ -83,38 +81,7 @@ public class ClusterConfig extends HelixProperty {
DISABLED_INSTANCES,
// Specifies job types and used for quota allocation
- QUOTA_TYPES,
-
- /**
- * Configurable characteristics of the WAGED rebalancer.
- * TODO: Split the WAGED rebalancer configuration items to the other config file.
- */
- // The required instance capacity keys for resource partition assignment calculation.
- INSTANCE_CAPACITY_KEYS,
- // The default instance capacity if no capacity is configured in the Instance Config node.
- DEFAULT_INSTANCE_CAPACITY_MAP,
- // The default partition weights if no weight is configured in the Resource Config node.
- DEFAULT_PARTITION_WEIGHT_MAP,
- // The preference of the rebalance result.
- // EVENNESS - Evenness of the resource utilization, partition, and top state distribution.
- // LESS_MOVEMENT - the tendency of keeping the current assignment instead of moving the partition for optimal assignment.
- REBALANCE_PREFERENCE,
- // Specify if the WAGED rebalancer should asynchronously perform the global rebalance, which is
- // in general slower than the partial rebalance.
- // Note that asynchronous global rebalance calculation will reduce the controller rebalance
- // delay. But it may cause more partition movements. This is because the partial rebalance will
- // be performed with a stale baseline. The rebalance result would be an intermediate one and
- // could be changed again when a new baseline is calculated.
- // For more details, please refer to
- // https://github.com/apache/helix/wiki/Weight-aware-Globally-Evenly-distributed-Rebalancer#rebalance-coordinator
- //
- // Default to be true.
- GLOBAL_REBALANCE_ASYNC_MODE
- }
-
- public enum GlobalRebalancePreferenceKey {
- EVENNESS,
- LESS_MOVEMENT
+ QUOTA_TYPES
}
private final static int DEFAULT_MAX_CONCURRENT_TASK_PER_INSTANCE = 40;
@@ -128,16 +95,6 @@ public class ClusterConfig extends HelixProperty {
public final static String TASK_QUOTA_RATIO_NOT_SET = "-1";
- // Default preference for all the aspects should be the same to ensure balanced setup.
- public final static Map<GlobalRebalancePreferenceKey, Integer>
- DEFAULT_GLOBAL_REBALANCE_PREFERENCE =
- ImmutableMap.<GlobalRebalancePreferenceKey, Integer>builder()
- .put(GlobalRebalancePreferenceKey.EVENNESS, 1)
- .put(GlobalRebalancePreferenceKey.LESS_MOVEMENT, 1).build();
- private final static int MAX_REBALANCE_PREFERENCE = 10;
- private final static int MIN_REBALANCE_PREFERENCE = 0;
- public final static boolean DEFAULT_GLOBAL_REBALANCE_ASYNC_MODE_ENABLED = true;
-
/**
* Instantiate for a specific cluster
* @param cluster the cluster identifier
@@ -156,21 +113,21 @@ public class ClusterConfig extends HelixProperty {
/**
* Set task quota type with the ratio of this quota.
- * @param quotaType String
+ * @param quotaType String
* @param quotaRatio int
*/
public void setTaskQuotaRatio(String quotaType, int quotaRatio) {
if (_record.getMapField(ClusterConfigProperty.QUOTA_TYPES.name()) == null) {
_record.setMapField(ClusterConfigProperty.QUOTA_TYPES.name(), new HashMap<String, String>());
}
- _record.getMapField(ClusterConfigProperty.QUOTA_TYPES.name())
- .put(quotaType, Integer.toString(quotaRatio));
+ _record.getMapField(ClusterConfigProperty.QUOTA_TYPES.name()).put(quotaType,
+ Integer.toString(quotaRatio));
}
/**
* Set task quota type with the ratio of this quota. Quota ratio must be a String that is
* parse-able into an int.
- * @param quotaType String
+ * @param quotaType String
* @param quotaRatio String
*/
public void setTaskQuotaRatio(String quotaType, String quotaRatio) {
@@ -253,8 +210,8 @@ public class ClusterConfig extends HelixProperty {
* @return
*/
public Boolean isPersistIntermediateAssignment() {
- return _record
- .getBooleanField(ClusterConfigProperty.PERSIST_INTERMEDIATE_ASSIGNMENT.toString(), false);
+ return _record.getBooleanField(ClusterConfigProperty.PERSIST_INTERMEDIATE_ASSIGNMENT.toString(),
+ false);
}
/**
@@ -276,8 +233,8 @@ public class ClusterConfig extends HelixProperty {
}
public Boolean isPipelineTriggersDisabled() {
- return _record
- .getBooleanField(ClusterConfigProperty.HELIX_DISABLE_PIPELINE_TRIGGERS.toString(), false);
+ return _record.getBooleanField(ClusterConfigProperty.HELIX_DISABLE_PIPELINE_TRIGGERS.toString(),
+ false);
}
/**
@@ -446,8 +403,8 @@ public class ClusterConfig extends HelixProperty {
* @return
*/
public int getNumOfflineInstancesForAutoExit() {
- return _record
- .getIntField(ClusterConfigProperty.NUM_OFFLINE_INSTANCES_FOR_AUTO_EXIT.name(), -1);
+ return _record.getIntField(ClusterConfigProperty.NUM_OFFLINE_INSTANCES_FOR_AUTO_EXIT.name(),
+ -1);
}
/**
@@ -487,7 +444,9 @@ public class ClusterConfig extends HelixProperty {
if (obj instanceof ClusterConfig) {
ClusterConfig that = (ClusterConfig) obj;
- return this.getId().equals(that.getId());
+ if (this.getId().equals(that.getId())) {
+ return true;
+ }
}
return false;
}
@@ -531,8 +490,8 @@ public class ClusterConfig extends HelixProperty {
}
if (!configStrs.isEmpty()) {
- _record
- .setListField(ClusterConfigProperty.STATE_TRANSITION_THROTTLE_CONFIGS.name(), configStrs);
+ _record.setListField(ClusterConfigProperty.STATE_TRANSITION_THROTTLE_CONFIGS.name(),
+ configStrs);
}
}
@@ -620,7 +579,7 @@ public class ClusterConfig extends HelixProperty {
public int getErrorPartitionThresholdForLoadBalance() {
return _record.getIntField(
ClusterConfigProperty.ERROR_PARTITION_THRESHOLD_FOR_LOAD_BALANCE.name(),
- DEFAULT_ERROR_PARTITION_THRESHOLD_FOR_LOAD_BALANCE);
+ DEFAULT_ERROR_PARTITION_THRESHOLD_FOR_LOAD_BALANCE);
}
/**
@@ -699,159 +658,6 @@ public class ClusterConfig extends HelixProperty {
}
/**
- * Set the required Instance Capacity Keys.
- * @param capacityKeys
- */
- public void setInstanceCapacityKeys(List<String> capacityKeys) {
- if (capacityKeys == null || capacityKeys.isEmpty()) {
- throw new IllegalArgumentException("The input instance capacity key list is empty.");
- }
- _record.setListField(ClusterConfigProperty.INSTANCE_CAPACITY_KEYS.name(), capacityKeys);
- }
-
- /**
- * @return The required Instance Capacity Keys. If not configured, return an empty list.
- */
- public List<String> getInstanceCapacityKeys() {
- List<String> capacityKeys = _record.getListField(ClusterConfigProperty.INSTANCE_CAPACITY_KEYS.name());
- if (capacityKeys == null) {
- return Collections.emptyList();
- }
- return capacityKeys;
- }
-
- /**
- * Get the default instance capacity information from the map fields.
- *
- * @return data map if it exists, or empty map
- */
- public Map<String, Integer> getDefaultInstanceCapacityMap() {
- return getDefaultCapacityMap(ClusterConfigProperty.DEFAULT_INSTANCE_CAPACITY_MAP);
- }
-
- /**
- * Set the default instance capacity information with an Integer mapping.
- * This information is required by the global rebalancer.
- * @see <a href="Rebalance Algorithm">
- * https://github.com/apache/helix/wiki/Design-Proposal---Weight-Aware-Globally-Even-Distribute-Rebalancer#rebalance-algorithm-adapter
- * </a>
- * If the instance capacity is not configured in either Instance Config nor Cluster Config, the
- * cluster topology is considered invalid. So the rebalancer may stop working.
- * @param capacityDataMap - map of instance capacity data
- * @throws IllegalArgumentException - when any of the data value is a negative number or when the map is empty
- */
- public void setDefaultInstanceCapacityMap(Map<String, Integer> capacityDataMap)
- throws IllegalArgumentException {
- setDefaultCapacityMap(ClusterConfigProperty.DEFAULT_INSTANCE_CAPACITY_MAP, capacityDataMap);
- }
-
- /**
- * Get the default partition weight information from the map fields.
- *
- * @return data map if it exists, or empty map
- */
- public Map<String, Integer> getDefaultPartitionWeightMap() {
- return getDefaultCapacityMap(ClusterConfigProperty.DEFAULT_PARTITION_WEIGHT_MAP);
- }
-
- /**
- * Set the default partition weight information with an Integer mapping.
- * This information is required by the global rebalancer.
- * @see <a href="Rebalance Algorithm">
- * https://github.com/apache/helix/wiki/Design-Proposal---Weight-Aware-Globally-Even-Distribute-Rebalancer#rebalance-algorithm-adapter
- * </a>
- * If the partition weight is not configured in either Resource Config nor Cluster Config, the
- * cluster topology is considered invalid. So the rebalancer may stop working.
- * @param weightDataMap - map of partition weight data
- * @throws IllegalArgumentException - when any of the data value is a negative number or when the map is empty
- */
- public void setDefaultPartitionWeightMap(Map<String, Integer> weightDataMap)
- throws IllegalArgumentException {
- setDefaultCapacityMap(ClusterConfigProperty.DEFAULT_PARTITION_WEIGHT_MAP, weightDataMap);
- }
-
- private Map<String, Integer> getDefaultCapacityMap(ClusterConfigProperty capacityPropertyType) {
- Map<String, String> capacityData = _record.getMapField(capacityPropertyType.name());
- if (capacityData != null) {
- return capacityData.entrySet().stream().collect(
- Collectors.toMap(entry -> entry.getKey(), entry -> Integer.parseInt(entry.getValue())));
- }
- return Collections.emptyMap();
- }
-
- private void setDefaultCapacityMap(ClusterConfigProperty capacityPropertyType,
- Map<String, Integer> capacityDataMap) throws IllegalArgumentException {
- if (capacityDataMap == null) {
- throw new IllegalArgumentException("Default capacity data is null");
- }
- Map<String, String> data = new HashMap<>();
- capacityDataMap.entrySet().stream().forEach(entry -> {
- if (entry.getValue() < 0) {
- throw new IllegalArgumentException(String
- .format("Default capacity data contains a negative value: %s = %d", entry.getKey(),
- entry.getValue()));
- }
- data.put(entry.getKey(), Integer.toString(entry.getValue()));
- });
- _record.setMapField(capacityPropertyType.name(), data);
- }
-
- /**
- * Set the global rebalancer's assignment preference.
- * @param preference A map of the GlobalRebalancePreferenceKey and the corresponding weight.
- * The ratio of the configured weights will determine the rebalancer's behavior.
- */
- public void setGlobalRebalancePreference(Map<GlobalRebalancePreferenceKey, Integer> preference) {
- Map<String, String> preferenceMap = new HashMap<>();
-
- preference.entrySet().stream().forEach(entry -> {
- if (entry.getValue() > MAX_REBALANCE_PREFERENCE
- || entry.getValue() < MIN_REBALANCE_PREFERENCE) {
- throw new IllegalArgumentException(String
- .format("Invalid global rebalance preference configuration. Key %s, Value %d.",
- entry.getKey().name(), entry.getValue()));
- }
- preferenceMap.put(entry.getKey().name(), Integer.toString(entry.getValue()));
- });
-
- _record.setMapField(ClusterConfigProperty.REBALANCE_PREFERENCE.name(), preferenceMap);
- }
-
- /**
- * Get the global rebalancer's assignment preference.
- */
- public Map<GlobalRebalancePreferenceKey, Integer> getGlobalRebalancePreference() {
- Map<String, String> preferenceStrMap =
- _record.getMapField(ClusterConfigProperty.REBALANCE_PREFERENCE.name());
- if (preferenceStrMap != null && !preferenceStrMap.isEmpty()) {
- Map<GlobalRebalancePreferenceKey, Integer> preference = new HashMap<>();
- for (GlobalRebalancePreferenceKey key : GlobalRebalancePreferenceKey.values()) {
- if (!preferenceStrMap.containsKey(key.name())) {
- // If any key is not configured with a value, return the default config.
- return DEFAULT_GLOBAL_REBALANCE_PREFERENCE;
- }
- preference.put(key, Integer.parseInt(preferenceStrMap.get(key.name())));
- }
- return preference;
- }
- // If configuration is not complete, return the default one.
- return DEFAULT_GLOBAL_REBALANCE_PREFERENCE;
- }
-
- /**
- * Set the asynchronous global rebalance mode.
- * @param isAsync true if the global rebalance should be performed asynchronously
- */
- public void setGlobalRebalanceAsyncMode(boolean isAsync) {
- _record.setBooleanField(ClusterConfigProperty.GLOBAL_REBALANCE_ASYNC_MODE.name(), isAsync);
- }
-
- public boolean isGlobalRebalanceAsyncModeEnabled() {
- return _record.getBooleanField(ClusterConfigProperty.GLOBAL_REBALANCE_ASYNC_MODE.name(),
- DEFAULT_GLOBAL_REBALANCE_ASYNC_MODE_ENABLED);
- }
-
- /**
* Get IdealState rules defined in the cluster config.
* @return
*/
diff --git a/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java b/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
index b55ba83..4d01766 100644
--- a/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
@@ -27,7 +27,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.stream.Collectors;
import com.google.common.base.Splitter;
import org.apache.helix.HelixException;
@@ -55,8 +54,7 @@ public class InstanceConfig extends HelixProperty {
INSTANCE_WEIGHT,
DOMAIN,
DELAY_REBALANCE_ENABLED,
- MAX_CONCURRENT_TASK,
- INSTANCE_CAPACITY_MAP
+ MAX_CONCURRENT_TASK
}
public static final int WEIGHT_NOT_SET = -1;
@@ -506,54 +504,6 @@ public class InstanceConfig extends HelixProperty {
_record.setIntField(InstanceConfigProperty.MAX_CONCURRENT_TASK.name(), maxConcurrentTask);
}
- /**
- * Get the instance capacity information from the map fields.
- * @return data map if it exists, or empty map
- */
- public Map<String, Integer> getInstanceCapacityMap() {
- Map<String, String> capacityData =
- _record.getMapField(InstanceConfigProperty.INSTANCE_CAPACITY_MAP.name());
-
- if (capacityData != null) {
- return capacityData.entrySet().stream().collect(
- Collectors.toMap(entry -> entry.getKey(), entry -> Integer.parseInt(entry.getValue())));
- }
- return Collections.emptyMap();
- }
-
- /**
- * Set the instance capacity information with an Integer mapping.
- * @param capacityDataMap - map of instance capacity data
- * @throws IllegalArgumentException - when any of the data value is a negative number or when the map is incomplete
- *
- * This information is required by the global rebalancer.
- * @see <a href="Rebalance Algorithm">
- * https://github.com/apache/helix/wiki/Design-Proposal---Weight-Aware-Globally-Even-Distribute-Rebalancer#rebalance-algorithm-adapter
- * </a>
- * If the instance capacity is not configured in neither Instance Config nor Cluster Config, the
- * cluster topology is considered invalid. So the rebalancer may stop working.
- * Note that when a rebalancer requires this capacity information, it will ignore INSTANCE_WEIGHT.
- */
- public void setInstanceCapacityMap(Map<String, Integer> capacityDataMap)
- throws IllegalArgumentException {
- if (capacityDataMap == null) {
- throw new IllegalArgumentException("Capacity Data is null");
- }
-
- Map<String, String> capacityData = new HashMap<>();
-
- capacityDataMap.entrySet().stream().forEach(entry -> {
- if (entry.getValue() < 0) {
- throw new IllegalArgumentException(String
- .format("Capacity Data contains a negative value: %s = %d", entry.getKey(),
- entry.getValue()));
- }
- capacityData.put(entry.getKey(), Integer.toString(entry.getValue()));
- });
-
- _record.setMapField(InstanceConfigProperty.INSTANCE_CAPACITY_MAP.name(), capacityData);
- }
-
@Override
public boolean equals(Object obj) {
if (obj instanceof InstanceConfig) {
diff --git a/helix-core/src/main/java/org/apache/helix/model/ResourceConfig.java b/helix-core/src/main/java/org/apache/helix/model/ResourceConfig.java
index 9cdb673..c37a594 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ResourceConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ResourceConfig.java
@@ -19,9 +19,7 @@ package org.apache.helix.model;
* under the License.
*/
-import java.io.IOException;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -31,8 +29,6 @@ import org.apache.helix.ZNRecord;
import org.apache.helix.api.config.HelixConfigProperty;
import org.apache.helix.api.config.RebalanceConfig;
import org.apache.helix.api.config.StateTransitionTimeoutConfig;
-import org.codehaus.jackson.map.ObjectMapper;
-import org.codehaus.jackson.type.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -57,8 +53,7 @@ public class ResourceConfig extends HelixProperty {
RESOURCE_TYPE,
GROUP_ROUTING_ENABLED,
EXTERNAL_VIEW_DISABLED,
- DELAY_REBALANCE_ENABLED,
- PARTITION_CAPACITY_MAP
+ DELAY_REBALANCE_ENABLED
}
public enum ResourceConfigConstants {
@@ -66,10 +61,6 @@ public class ResourceConfig extends HelixProperty {
}
private static final Logger _logger = LoggerFactory.getLogger(ResourceConfig.class.getName());
- private static final ObjectMapper _objectMapper = new ObjectMapper();
-
- public static final String DEFAULT_PARTITION_KEY = "DEFAULT";
-
/**
* Instantiate for a specific instance
*
@@ -101,24 +92,10 @@ public class ResourceConfig extends HelixProperty {
String stateModelDefRef, String stateModelFactoryName, String numReplica,
int minActiveReplica, int maxPartitionsPerInstance, String instanceGroupTag,
Boolean helixEnabled, String resourceGroupName, String resourceType,
- Boolean groupRoutingEnabled, Boolean externalViewDisabled, RebalanceConfig rebalanceConfig,
- StateTransitionTimeoutConfig stateTransitionTimeoutConfig,
+ Boolean groupRoutingEnabled, Boolean externalViewDisabled,
+ RebalanceConfig rebalanceConfig, StateTransitionTimeoutConfig stateTransitionTimeoutConfig,
Map<String, List<String>> listFields, Map<String, Map<String, String>> mapFields,
Boolean p2pMessageEnabled) {
- this(resourceId, monitorDisabled, numPartitions, stateModelDefRef, stateModelFactoryName,
- numReplica, minActiveReplica, maxPartitionsPerInstance, instanceGroupTag, helixEnabled,
- resourceGroupName, resourceType, groupRoutingEnabled, externalViewDisabled, rebalanceConfig,
- stateTransitionTimeoutConfig, listFields, mapFields, p2pMessageEnabled, null);
- }
-
- private ResourceConfig(String resourceId, Boolean monitorDisabled, int numPartitions,
- String stateModelDefRef, String stateModelFactoryName, String numReplica,
- int minActiveReplica, int maxPartitionsPerInstance, String instanceGroupTag,
- Boolean helixEnabled, String resourceGroupName, String resourceType,
- Boolean groupRoutingEnabled, Boolean externalViewDisabled,
- RebalanceConfig rebalanceConfig, StateTransitionTimeoutConfig stateTransitionTimeoutConfig,
- Map<String, List<String>> listFields, Map<String, Map<String, String>> mapFields,
- Boolean p2pMessageEnabled, Map<String, Map<String, Integer>> partitionCapacityMap) {
super(resourceId);
if (monitorDisabled != null) {
@@ -195,15 +172,6 @@ public class ResourceConfig extends HelixProperty {
if (mapFields != null) {
_record.setMapFields(mapFields);
}
-
- if (partitionCapacityMap != null) {
- try {
- setPartitionCapacityMap(partitionCapacityMap);
- } catch (IOException e) {
- throw new IllegalArgumentException(
- "Failed to set partition capacity. Invalid capacity configuration.");
- }
- }
}
@@ -382,64 +350,6 @@ public class ResourceConfig extends HelixProperty {
}
/**
- * Get the partition capacity information from a JSON among the map fields.
- * <PartitionName or DEFAULT_PARTITION_KEY, <Capacity Key, Capacity Number>>
- *
- * @return data map if it exists, or empty map
- * @throws IOException - when JSON conversion fails
- */
- public Map<String, Map<String, Integer>> getPartitionCapacityMap() throws IOException {
- Map<String, String> partitionCapacityData =
- _record.getMapField(ResourceConfigProperty.PARTITION_CAPACITY_MAP.name());
- Map<String, Map<String, Integer>> partitionCapacityMap = new HashMap<>();
- if (partitionCapacityData != null) {
- for (String partition : partitionCapacityData.keySet()) {
- Map<String, Integer> capacities = _objectMapper
- .readValue(partitionCapacityData.get(partition),
- new TypeReference<Map<String, Integer>>() {
- });
- partitionCapacityMap.put(partition, capacities);
- }
- }
- return partitionCapacityMap;
- }
-
- /**
- * Set the partition capacity information with a map <PartitionName or DEFAULT_PARTITION_KEY, <Capacity Key, Capacity Number>>
- *
- * @param partitionCapacityMap - map of partition capacity data
- * @throws IllegalArgumentException - when any of the data value is a negative number or map is incomplete
- * @throws IOException - when JSON parsing fails
- */
- public void setPartitionCapacityMap(Map<String, Map<String, Integer>> partitionCapacityMap)
- throws IllegalArgumentException, IOException {
- if (partitionCapacityMap == null) {
- throw new IllegalArgumentException("Capacity Map is null");
- }
- if (!partitionCapacityMap.containsKey(DEFAULT_PARTITION_KEY)) {
- throw new IllegalArgumentException(String
- .format("The default partition capacity with the default key %s is required.",
- DEFAULT_PARTITION_KEY));
- }
-
- Map<String, String> newCapacityRecord = new HashMap<>();
- for (String partition : partitionCapacityMap.keySet()) {
- Map<String, Integer> capacities = partitionCapacityMap.get(partition);
- // Verify the input is valid
- if (capacities.isEmpty()) {
- throw new IllegalArgumentException("Capacity Data is empty");
- }
- if (capacities.entrySet().stream().anyMatch(entry -> entry.getValue() < 0)) {
- throw new IllegalArgumentException(
- String.format("Capacity Data contains a negative value:%s", capacities.toString()));
- }
- newCapacityRecord.put(partition, _objectMapper.writeValueAsString(capacities));
- }
-
- _record.setMapField(ResourceConfigProperty.PARTITION_CAPACITY_MAP.name(), newCapacityRecord);
- }
-
- /**
* Put a set of simple configs.
*
* @param configsMap
@@ -566,7 +476,6 @@ public class ResourceConfig extends HelixProperty {
private StateTransitionTimeoutConfig _stateTransitionTimeoutConfig;
private Map<String, List<String>> _preferenceLists;
private Map<String, Map<String, String>> _mapFields;
- private Map<String, Map<String, Integer>> _partitionCapacityMap;
public Builder(String resourceId) {
_resourceId = resourceId;
@@ -755,23 +664,6 @@ public class ResourceConfig extends HelixProperty {
return _preferenceLists;
}
- public Builder setPartitionCapacity(Map<String, Integer> defaultCapacity) {
- setPartitionCapacity(DEFAULT_PARTITION_KEY, defaultCapacity);
- return this;
- }
-
- public Builder setPartitionCapacity(String partition, Map<String, Integer> capacity) {
- if (_partitionCapacityMap == null) {
- _partitionCapacityMap = new HashMap<>();
- }
- _partitionCapacityMap.put(partition, capacity);
- return this;
- }
-
- public Map<String, Integer> getPartitionCapacity(String partition) {
- return _partitionCapacityMap.get(partition);
- }
-
public Builder setMapField(String key, Map<String, String> fields) {
if (_mapFields == null) {
_mapFields = new TreeMap<>();
@@ -816,19 +708,6 @@ public class ResourceConfig extends HelixProperty {
}
}
}
-
- if (_partitionCapacityMap != null) {
- if (_partitionCapacityMap.keySet().stream()
- .noneMatch(partition -> partition.equals(DEFAULT_PARTITION_KEY))) {
- throw new IllegalArgumentException(
- "Partition capacity is configured without the DEFAULT capacity!");
- }
- if (_partitionCapacityMap.values().stream()
- .anyMatch(capacity -> capacity.values().stream().anyMatch(value -> value < 0))) {
- throw new IllegalArgumentException(
- "Partition capacity is configured with negative capacity value!");
- }
- }
}
public ResourceConfig build() {
@@ -839,7 +718,7 @@ public class ResourceConfig extends HelixProperty {
_stateModelFactoryName, _numReplica, _minActiveReplica, _maxPartitionsPerInstance,
_instanceGroupTag, _helixEnabled, _resourceGroupName, _resourceType, _groupRoutingEnabled,
_externalViewDisabled, _rebalanceConfig, _stateTransitionTimeoutConfig, _preferenceLists,
- _mapFields, _p2pMessageEnabled, _partitionCapacityMap);
+ _mapFields, _p2pMessageEnabled);
}
}
}
diff --git a/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java b/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java
index 0a40331..ae59522 100644
--- a/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java
+++ b/helix-core/src/main/java/org/apache/helix/model/StateModelDefinition.java
@@ -46,8 +46,6 @@ public class StateModelDefinition extends HelixProperty {
STATE_PRIORITY_LIST
}
- public static final int TOP_STATE_PRIORITY = 1;
-
/**
* state model's initial state
*/
@@ -100,7 +98,7 @@ public class StateModelDefinition extends HelixProperty {
_stateTransitionTable = new HashMap<>();
_statesCountMap = new HashMap<>();
if (_statesPriorityList != null) {
- int priority = TOP_STATE_PRIORITY;
+ int priority = 1;
for (String state : _statesPriorityList) {
Map<String, String> metaData = record.getMapField(state + ".meta");
if (metaData != null) {
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ClusterStatusMonitor.java b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ClusterStatusMonitor.java
index fc0b19d..d6c3bb2 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ClusterStatusMonitor.java
+++ b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ClusterStatusMonitor.java
@@ -236,28 +236,27 @@ public class ClusterStatusMonitor implements ClusterStatusMonitorMBean {
// Unregister beans for instances that are no longer configured
Set<String> toUnregister = Sets.newHashSet(_instanceMonitorMap.keySet());
toUnregister.removeAll(instanceSet);
- unregisterInstances(toUnregister);
+ try {
+ unregisterInstances(toUnregister);
+ } catch (MalformedObjectNameException e) {
+ LOG.error("Could not unregister instances from MBean server: " + toUnregister, e);
+ }
// Register beans for instances that are newly configured
Set<String> toRegister = Sets.newHashSet(instanceSet);
toRegister.removeAll(_instanceMonitorMap.keySet());
Set<InstanceMonitor> monitorsToRegister = Sets.newHashSet();
for (String instanceName : toRegister) {
- try {
- ObjectName objectName = getObjectName(getInstanceBeanName(instanceName));
- InstanceMonitor bean = new InstanceMonitor(_clusterName, instanceName, objectName);
- bean.updateInstance(tags.get(instanceName), disabledPartitions.get(instanceName),
- oldDisabledPartitions.get(instanceName), liveInstanceSet.contains(instanceName),
- !disabledInstanceSet.contains(instanceName));
- monitorsToRegister.add(bean);
- } catch (MalformedObjectNameException ex) {
- LOG.error("Failed to create instance monitor for instance: {}.", instanceName);
- }
+ InstanceMonitor bean = new InstanceMonitor(_clusterName, instanceName);
+ bean.updateInstance(tags.get(instanceName), disabledPartitions.get(instanceName),
+ oldDisabledPartitions.get(instanceName), liveInstanceSet.contains(instanceName),
+ !disabledInstanceSet.contains(instanceName));
+ monitorsToRegister.add(bean);
}
try {
registerInstances(monitorsToRegister);
- } catch (JMException e) {
- LOG.error("Could not register instances with MBean server: {}.", toRegister, e);
+ } catch (MalformedObjectNameException e) {
+ LOG.error("Could not register instances with MBean server: " + toRegister, e);
}
// Update all the sets
@@ -283,8 +282,8 @@ public class ClusterStatusMonitor implements ClusterStatusMonitorMBean {
try {
unregisterInstances(Arrays.asList(instanceName));
registerInstances(Arrays.asList(bean));
- } catch (JMException e) {
- LOG.error("Could not refresh registration with MBean server: {}", instanceName, e);
+ } catch (MalformedObjectNameException e) {
+ LOG.error("Could not refresh registration with MBean server: " + instanceName, e);
}
}
}
@@ -367,28 +366,6 @@ public class ClusterStatusMonitor implements ClusterStatusMonitorMBean {
}
/**
- * Updates instance capacity status for per instance, including max usage and capacity of each
- * capacity key. Before calling this API, we assume the instance monitors are already registered
- * in ReadClusterDataStage. If the monitor is not registered, this instance capacity status update
- * will fail.
- *
- * @param instanceName This instance name
- * @param maxUsage Max capacity usage of this instance
- * @param capacityMap A map of this instance capacity, {capacity key: capacity value}
- */
- public void updateInstanceCapacityStatus(String instanceName, double maxUsage,
- Map<String, Integer> capacityMap) {
- InstanceMonitor monitor = _instanceMonitorMap.get(instanceName);
- if (monitor == null) {
- LOG.warn("Failed to update instance capacity status because instance monitor is not found, "
- + "instance: {}.", instanceName);
- return;
- }
- monitor.updateMaxCapacityUsage(maxUsage);
- monitor.updateCapacity(capacityMap);
- }
-
- /**
* Update gauges for resource at instance level
* @param bestPossibleStates
* @param resourceMap
@@ -497,25 +474,6 @@ public class ClusterStatusMonitor implements ClusterStatusMonitorMBean {
}
}
- /**
- * Updates metrics of average partition weight per capacity key for a resource. If a resource
- * monitor is not yet existed for this resource, a new resource monitor will be created for this
- * resource.
- *
- * @param resourceName The resource name for which partition weight is updated
- * @param averageWeightMap A map of average partition weight of each capacity key:
- * capacity key -> average partition weight
- */
- public void updatePartitionWeight(String resourceName, Map<String, Integer> averageWeightMap) {
- ResourceMonitor monitor = getOrCreateResourceMonitor(resourceName);
- if (monitor == null) {
- LOG.warn("Failed to update partition weight metric for resource: {} because resource monitor"
- + " is not created.", resourceName);
- return;
- }
- monitor.updatePartitionWeightStats(averageWeightMap);
- }
-
public void updateMissingTopStateDurationStats(String resourceName, long totalDuration,
long helixLatency, boolean isGraceful, boolean succeeded) {
ResourceMonitor resourceMonitor = getOrCreateResourceMonitor(resourceName);
@@ -736,35 +694,31 @@ public class ClusterStatusMonitor implements ClusterStatusMonitorMBean {
}
private void registerInstances(Collection<InstanceMonitor> instances)
- throws JMException {
+ throws MalformedObjectNameException {
synchronized (_instanceMonitorMap) {
for (InstanceMonitor monitor : instances) {
String instanceName = monitor.getInstanceName();
- // If this instance MBean is already registered, unregister it.
- InstanceMonitor removedMonitor = _instanceMonitorMap.remove(instanceName);
- if (removedMonitor != null) {
- removedMonitor.unregister();
- }
- monitor.register();
+ String beanName = getInstanceBeanName(instanceName);
+ register(monitor, getObjectName(beanName));
_instanceMonitorMap.put(instanceName, monitor);
}
}
}
- private void unregisterAllInstances() {
+ private void unregisterAllInstances() throws MalformedObjectNameException {
synchronized (_instanceMonitorMap) {
unregisterInstances(_instanceMonitorMap.keySet());
}
}
- private void unregisterInstances(Collection<String> instances) {
+ private void unregisterInstances(Collection<String> instances)
+ throws MalformedObjectNameException {
synchronized (_instanceMonitorMap) {
for (String instanceName : instances) {
- InstanceMonitor monitor = _instanceMonitorMap.remove(instanceName);
- if (monitor != null) {
- monitor.unregister();
- }
+ String beanName = getInstanceBeanName(instanceName);
+ unregister(getObjectName(beanName));
}
+ _instanceMonitorMap.keySet().removeAll(instances);
}
}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/InstanceMonitor.java b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/InstanceMonitor.java
index e0c0f89..dc43d48 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/InstanceMonitor.java
+++ b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/InstanceMonitor.java
@@ -23,105 +23,36 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import javax.management.JMException;
-import javax.management.ObjectName;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMBeanProvider;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMetric;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.SimpleDynamicMetric;
-
/**
* Implementation of the instance status bean
*/
-public class InstanceMonitor extends DynamicMBeanProvider {
- /**
- * Metric names for instance capacity.
- */
- public enum InstanceMonitorMetric {
- // TODO: change the metric names with Counter and Gauge suffix and deprecate old names.
- TOTAL_MESSAGE_RECEIVED_COUNTER("TotalMessageReceived"),
- ENABLED_STATUS_GAUGE("Enabled"),
- ONLINE_STATUS_GAUGE("Online"),
- DISABLED_PARTITIONS_GAUGE("DisabledPartitions"),
- MAX_CAPACITY_USAGE_GAUGE("MaxCapacityUsageGauge");
-
- private final String metricName;
-
- InstanceMonitorMetric(String name) {
- metricName = name;
- }
-
- public String metricName() {
- return metricName;
- }
- }
-
+public class InstanceMonitor implements InstanceMonitorMBean {
private final String _clusterName;
private final String _participantName;
- private final ObjectName _initObjectName;
-
private List<String> _tags;
-
- // Counters
- private SimpleDynamicMetric<Long> _totalMessagedReceivedCounter;
-
- // Gauges
- private SimpleDynamicMetric<Long> _enabledStatusGauge;
- private SimpleDynamicMetric<Long> _disabledPartitionsGauge;
- private SimpleDynamicMetric<Long> _onlineStatusGauge;
- private SimpleDynamicMetric<Double> _maxCapacityUsageGauge;
-
- // A map of dynamic capacity Gauges. The map's keys could change.
- private final Map<String, SimpleDynamicMetric<Long>> _dynamicCapacityMetricsMap;
+ private long _disabledPartitions;
+ private boolean _isUp;
+ private boolean _isEnabled;
+ private long _totalMessageReceived;
/**
* Initialize the bean
* @param clusterName the cluster to monitor
* @param participantName the instance whose statistics this holds
*/
- public InstanceMonitor(String clusterName, String participantName, ObjectName objectName) {
+ public InstanceMonitor(String clusterName, String participantName) {
_clusterName = clusterName;
_participantName = participantName;
_tags = ImmutableList.of(ClusterStatusMonitor.DEFAULT_TAG);
- _initObjectName = objectName;
- _dynamicCapacityMetricsMap = new ConcurrentHashMap<>();
-
- createMetrics();
- }
-
- private void createMetrics() {
- _totalMessagedReceivedCounter = new SimpleDynamicMetric<>(
- InstanceMonitorMetric.TOTAL_MESSAGE_RECEIVED_COUNTER.metricName(), 0L);
-
- _disabledPartitionsGauge =
- new SimpleDynamicMetric<>(InstanceMonitorMetric.DISABLED_PARTITIONS_GAUGE.metricName(),
- 0L);
- _enabledStatusGauge =
- new SimpleDynamicMetric<>(InstanceMonitorMetric.ENABLED_STATUS_GAUGE.metricName(), 0L);
- _onlineStatusGauge =
- new SimpleDynamicMetric<>(InstanceMonitorMetric.ONLINE_STATUS_GAUGE.metricName(), 0L);
- _maxCapacityUsageGauge =
- new SimpleDynamicMetric<>(InstanceMonitorMetric.MAX_CAPACITY_USAGE_GAUGE.metricName(),
- 0.0d);
- }
-
- private List<DynamicMetric<?, ?>> buildAttributeList() {
- List<DynamicMetric<?, ?>> attributeList = Lists.newArrayList(
- _totalMessagedReceivedCounter,
- _disabledPartitionsGauge,
- _enabledStatusGauge,
- _onlineStatusGauge,
- _maxCapacityUsageGauge
- );
-
- attributeList.addAll(_dynamicCapacityMetricsMap.values());
-
- return attributeList;
+ _disabledPartitions = 0L;
+ _isUp = false;
+ _isEnabled = false;
+ _totalMessageReceived = 0;
}
@Override
@@ -130,32 +61,44 @@ public class InstanceMonitor extends DynamicMBeanProvider {
serializedTags(), _participantName);
}
- protected long getOnline() {
- return _onlineStatusGauge.getValue();
+ @Override
+ public long getOnline() {
+ return _isUp ? 1 : 0;
}
- protected long getEnabled() {
- return _enabledStatusGauge.getValue();
+ @Override
+ public long getEnabled() {
+ return _isEnabled ? 1 : 0;
}
- protected long getTotalMessageReceived() {
- return _totalMessagedReceivedCounter.getValue();
+ @Override
+ public long getTotalMessageReceived() {
+ return _totalMessageReceived;
}
- protected long getDisabledPartitions() {
- return _disabledPartitionsGauge.getValue();
+ @Override
+ public long getDisabledPartitions() {
+ return _disabledPartitions;
+ }
+
+ /**
+ * Get all the tags currently on this instance
+ * @return list of tags
+ */
+ public List<String> getTags() {
+ return _tags;
}
/**
* Get the name of the monitored instance
* @return instance name as a string
*/
- protected String getInstanceName() {
+ public String getInstanceName() {
return _participantName;
}
private String serializedTags() {
- return Joiner.on('|').skipNulls().join(_tags);
+ return Joiner.on('|').skipNulls().join(_tags).toString();
}
/**
@@ -174,22 +117,20 @@ public class InstanceMonitor extends DynamicMBeanProvider {
_tags = Lists.newArrayList(tags);
Collections.sort(_tags);
}
- long numDisabledPartitions = 0L;
+ _disabledPartitions = 0L;
if (disabledPartitions != null) {
for (List<String> partitions : disabledPartitions.values()) {
if (partitions != null) {
- numDisabledPartitions += partitions.size();
+ _disabledPartitions += partitions.size();
}
}
}
// TODO : Get rid of this when old API removed.
if (oldDisabledPartitions != null) {
- numDisabledPartitions += oldDisabledPartitions.size();
+ _disabledPartitions += oldDisabledPartitions.size();
}
-
- _onlineStatusGauge.updateValue(isLive ? 1L : 0L);
- _enabledStatusGauge.updateValue(isEnabled ? 1L : 0L);
- _disabledPartitionsGauge.updateValue(numDisabledPartitions);
+ _isUp = isLive;
+ _isEnabled = isEnabled;
}
/**
@@ -197,64 +138,7 @@ public class InstanceMonitor extends DynamicMBeanProvider {
* @param messageReceived received message numbers
*/
public synchronized void increaseMessageCount(long messageReceived) {
- _totalMessagedReceivedCounter
- .updateValue(_totalMessagedReceivedCounter.getValue() + messageReceived);
- }
-
- /**
- * Updates max capacity usage for this instance.
- * @param maxUsage max capacity usage of this instance
- */
- public synchronized void updateMaxCapacityUsage(double maxUsage) {
- _maxCapacityUsageGauge.updateValue(maxUsage);
- }
-
- /**
- * Gets max capacity usage of this instance.
- * @return Max capacity usage of this instance.
- */
- protected synchronized double getMaxCapacityUsageGauge() {
- return _maxCapacityUsageGauge.getValue();
- }
-
- /**
- * Updates instance capacity metrics.
- * @param capacity A map of instance capacity.
- */
- public void updateCapacity(Map<String, Integer> capacity) {
- synchronized (_dynamicCapacityMetricsMap) {
- // If capacity keys don't have any change, we just update the metric values.
- if (_dynamicCapacityMetricsMap.keySet().equals(capacity.keySet())) {
- for (Map.Entry<String, Integer> entry : capacity.entrySet()) {
- _dynamicCapacityMetricsMap.get(entry.getKey()).updateValue((long) entry.getValue());
- }
- return;
- }
-
- // If capacity keys have any changes, we need to retain the capacity metrics.
- // Make sure capacity metrics map has the same capacity keys.
- // And update metrics values.
- _dynamicCapacityMetricsMap.keySet().retainAll(capacity.keySet());
- for (Map.Entry<String, Integer> entry : capacity.entrySet()) {
- String capacityName = entry.getKey();
- if (_dynamicCapacityMetricsMap.containsKey(capacityName)) {
- _dynamicCapacityMetricsMap.get(capacityName).updateValue((long) entry.getValue());
- } else {
- _dynamicCapacityMetricsMap.put(capacityName,
- new SimpleDynamicMetric<>(capacityName + "Gauge", (long) entry.getValue()));
- }
- }
- }
-
- // Update MBean's all attributes.
- updateAttributesInfo(buildAttributeList(),
- "Instance monitor for instance: " + getInstanceName());
+ _totalMessageReceived += messageReceived;
}
- @Override
- public DynamicMBeanProvider register() throws JMException {
- doRegister(buildAttributeList(), _initObjectName);
-
- return this;
- }
}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/Metric.java b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/InstanceMonitorMBean.java
similarity index 53%
rename from helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/Metric.java
rename to helix-core/src/main/java/org/apache/helix/monitoring/mbeans/InstanceMonitorMBean.java
index be7ea80..a3221d8 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/Metric.java
+++ b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/InstanceMonitorMBean.java
@@ -1,4 +1,4 @@
-package org.apache.helix.monitoring.metrics.model;
+package org.apache.helix.monitoring.mbeans;
/*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -19,32 +19,33 @@ package org.apache.helix.monitoring.metrics.model;
* under the License.
*/
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMetric;
+import org.apache.helix.monitoring.SensorNameProvider;
/**
- * Defines a generic metric interface.
- * @param <T> type of input value for the metric
+ * A basic bean describing the status of a single instance
*/
-public interface Metric<T> {
-
+public interface InstanceMonitorMBean extends SensorNameProvider {
/**
- * Gets the name of the metric.
+ * Check if this instance is live
+ * @return 1 if running, 0 otherwise
*/
- String getMetricName();
+ public long getOnline();
/**
- * Prints the metric along with its name.
+ * Check if this instance is enabled
+ * @return 1 if enabled, 0 if disabled
*/
- String toString();
+ public long getEnabled();
/**
- * Returns the most recently emitted value for the metric at the time of the call.
- * @return metric value
+ * Get total message received for this instances
+ * @return The total number of messages sent to this instance
*/
- T getLastEmittedMetricValue();
+ public long getTotalMessageReceived();
/**
- * Returns the underlying DynamicMetric.
+ * Get the total disabled partitions number for this instance
+ * @return The total number of disabled partitions
*/
- DynamicMetric getDynamicMetric();
+ public long getDisabledPartitions();
}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/MonitorDomainNames.java b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/MonitorDomainNames.java
index fee9099..73bf057 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/MonitorDomainNames.java
+++ b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/MonitorDomainNames.java
@@ -28,6 +28,5 @@ public enum MonitorDomainNames {
HelixThreadPoolExecutor,
HelixCallback,
RoutingTableProvider,
- CLMParticipantReport,
- Rebalancer
+ CLMParticipantReport
}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ResourceMonitor.java b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ResourceMonitor.java
index af9c318..d7a368e 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ResourceMonitor.java
+++ b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/ResourceMonitor.java
@@ -19,19 +19,18 @@ package org.apache.helix.monitoring.mbeans;
* under the License.
*/
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import javax.management.JMException;
import javax.management.ObjectName;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.SlidingTimeWindowArrayReservoir;
-import com.google.common.collect.Lists;
import org.apache.helix.HelixDefinedState;
import org.apache.helix.model.ExternalView;
import org.apache.helix.model.IdealState;
@@ -50,8 +49,6 @@ public class ResourceMonitor extends DynamicMBeanProvider {
INTERMEDIATE_STATE_CAL_FAILED
}
- private static final String GAUGE_METRIC_SUFFIX = "Gauge";
-
// Gauges
private SimpleDynamicMetric<Long> _numOfPartitions;
private SimpleDynamicMetric<Long> _numOfPartitionsInExternalView;
@@ -86,13 +83,31 @@ public class ResourceMonitor extends DynamicMBeanProvider {
private final String _clusterName;
private final ObjectName _initObjectName;
- // A map of dynamic capacity Gauges. The map's keys could change.
- private final Map<String, SimpleDynamicMetric<Long>> _dynamicCapacityMetricsMap;
-
@Override
- public DynamicMBeanProvider register() throws JMException {
- doRegister(buildAttributeList(), _initObjectName);
-
+ public ResourceMonitor register() throws JMException {
+ List<DynamicMetric<?, ?>> attributeList = new ArrayList<>();
+ attributeList.add(_numOfPartitions);
+ attributeList.add(_numOfPartitionsInExternalView);
+ attributeList.add(_numOfErrorPartitions);
+ attributeList.add(_numNonTopStatePartitions);
+ attributeList.add(_numLessMinActiveReplicaPartitions);
+ attributeList.add(_numLessReplicaPartitions);
+ attributeList.add(_numPendingRecoveryRebalancePartitions);
+ attributeList.add(_numPendingLoadRebalancePartitions);
+ attributeList.add(_numRecoveryRebalanceThrottledPartitions);
+ attributeList.add(_numLoadRebalanceThrottledPartitions);
+ attributeList.add(_externalViewIdealStateDiff);
+ attributeList.add(_successfulTopStateHandoffDurationCounter);
+ attributeList.add(_successTopStateHandoffCounter);
+ attributeList.add(_failedTopStateHandoffCounter);
+ attributeList.add(_maxSinglePartitionTopStateHandoffDuration);
+ attributeList.add(_partitionTopStateHandoffDurationGauge);
+ attributeList.add(_partitionTopStateHandoffHelixLatencyGauge);
+ attributeList.add(_partitionTopStateNonGracefulHandoffDurationGauge);
+ attributeList.add(_totalMessageReceived);
+ attributeList.add(_numPendingStateTransitions);
+ attributeList.add(_rebalanceState);
+ doRegister(attributeList, _initObjectName);
return this;
}
@@ -101,12 +116,10 @@ public class ResourceMonitor extends DynamicMBeanProvider {
}
@SuppressWarnings("unchecked")
- public ResourceMonitor(String clusterName, String resourceName, ObjectName objectName)
- throws JMException {
+ public ResourceMonitor(String clusterName, String resourceName, ObjectName objectName) {
_clusterName = clusterName;
_resourceName = resourceName;
_initObjectName = objectName;
- _dynamicCapacityMetricsMap = new ConcurrentHashMap<>();
_externalViewIdealStateDiff = new SimpleDynamicMetric("DifferenceWithIdealStateGauge", 0L);
_numLoadRebalanceThrottledPartitions =
@@ -369,36 +382,6 @@ public class ResourceMonitor extends DynamicMBeanProvider {
_numLoadRebalanceThrottledPartitions.updateValue(numLoadRebalanceThrottledPartitions);
}
- /**
- * Updates partition weight metric. If the partition capacity keys are changed, all MBean
- * attributes will be updated accordingly: old capacity keys will be replaced with new capacity
- * keys in MBean server.
- *
- * @param partitionWeightMap A map of partition weight: capacity key -> partition weight
- */
- void updatePartitionWeightStats(Map<String, Integer> partitionWeightMap) {
- synchronized (_dynamicCapacityMetricsMap) {
- if (_dynamicCapacityMetricsMap.keySet().equals(partitionWeightMap.keySet())) {
- for (Map.Entry<String, Integer> entry : partitionWeightMap.entrySet()) {
- _dynamicCapacityMetricsMap.get(entry.getKey()).updateValue((long) entry.getValue());
- }
- return;
- }
-
- // Capacity keys are changed, so capacity attribute map needs to be updated.
- _dynamicCapacityMetricsMap.clear();
- for (Map.Entry<String, Integer> entry : partitionWeightMap.entrySet()) {
- String capacityKey = entry.getKey();
- _dynamicCapacityMetricsMap.put(capacityKey,
- new SimpleDynamicMetric<>(capacityKey + GAUGE_METRIC_SUFFIX, (long) entry.getValue()));
- }
- }
-
- // Update all MBean attributes.
- updateAttributesInfo(buildAttributeList(),
- "Resource monitor for resource: " + getResourceName());
- }
-
public void setRebalanceState(RebalanceStatus state) {
_rebalanceState.updateValue(state.name());
}
@@ -445,34 +428,4 @@ public class ResourceMonitor extends DynamicMBeanProvider {
_lastResetTime = System.currentTimeMillis();
}
}
-
- private List<DynamicMetric<?, ?>> buildAttributeList() {
- List<DynamicMetric<?, ?>> attributeList = Lists.newArrayList(
- _numOfPartitions,
- _numOfPartitionsInExternalView,
- _numOfErrorPartitions,
- _numNonTopStatePartitions,
- _numLessMinActiveReplicaPartitions,
- _numLessReplicaPartitions,
- _numPendingRecoveryRebalancePartitions,
- _numPendingLoadRebalancePartitions,
- _numRecoveryRebalanceThrottledPartitions,
- _numLoadRebalanceThrottledPartitions,
- _externalViewIdealStateDiff,
- _successfulTopStateHandoffDurationCounter,
- _successTopStateHandoffCounter,
- _failedTopStateHandoffCounter,
- _maxSinglePartitionTopStateHandoffDuration,
- _partitionTopStateHandoffDurationGauge,
- _partitionTopStateHandoffHelixLatencyGauge,
- _partitionTopStateNonGracefulHandoffDurationGauge,
- _totalMessageReceived,
- _numPendingStateTransitions,
- _rebalanceState
- );
-
- attributeList.addAll(_dynamicCapacityMetricsMap.values());
-
- return attributeList;
- }
}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMBeanProvider.java b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMBeanProvider.java
index 407a714..0ce0b44 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMBeanProvider.java
+++ b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/DynamicMBeanProvider.java
@@ -22,19 +22,23 @@ package org.apache.helix.monitoring.mbeans.dynamicMBeans;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
+import javax.management.InvalidAttributeValueException;
import javax.management.JMException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.ObjectName;
+import javax.management.ReflectionException;
import org.apache.helix.SystemPropertyKeys;
import org.apache.helix.monitoring.SensorNameProvider;
@@ -49,12 +53,12 @@ import org.slf4j.LoggerFactory;
public abstract class DynamicMBeanProvider implements DynamicMBean, SensorNameProvider {
protected final Logger _logger = LoggerFactory.getLogger(getClass());
protected static final long DEFAULT_RESET_INTERVAL_MS = 60 * 60 * 1000; // Reset time every hour
- private static final String SENSOR_NAME_TAG = "SensorName";
- private static final String DEFAULT_DESCRIPTION =
+ private static String SENSOR_NAME_TAG = "SensorName";
+ private static String DEFAULT_DESCRIPTION =
"Information on the management interface of the MBean";
// Attribute name to the DynamicMetric object mapping
- private Map<String, DynamicMetric> _attributeMap = new HashMap<>();
+ private final Map<String, DynamicMetric> _attributeMap = new HashMap<>();
private ObjectName _objectName = null;
private MBeanInfo _mBeanInfo;
@@ -84,7 +88,7 @@ public abstract class DynamicMBeanProvider implements DynamicMBean, SensorNamePr
objectName.getCanonicalName());
return false;
}
- updateAttributesInfo(dynamicMetrics, description);
+ updateAttributtInfos(dynamicMetrics, description);
_objectName = MBeanRegistrar.register(this, objectName);
return true;
}
@@ -95,30 +99,26 @@ public abstract class DynamicMBeanProvider implements DynamicMBean, SensorNamePr
}
/**
- * Updates the Dynamic MBean provider with new metric list.
- * If the pass-in metrics collection is empty, the original attributes will be removed.
- *
+ * Update the Dynamic MBean provider with new metric list.
* @param description description of the MBean
- * @param dynamicMetrics the DynamicMetrics. Empty collection will remove the metric attributes.
+ * @param dynamicMetrics the DynamicMetrics
*/
- protected void updateAttributesInfo(Collection<DynamicMetric<?, ?>> dynamicMetrics,
+ private void updateAttributtInfos(Collection<DynamicMetric<?, ?>> dynamicMetrics,
String description) {
- if (dynamicMetrics == null) {
- _logger.warn("Cannot update attributes info because dynamicMetrics is null.");
- return;
- }
+ _attributeMap.clear();
+ // get all attributes that can be emit by the dynamicMetrics.
List<MBeanAttributeInfo> attributeInfoList = new ArrayList<>();
- // Use a new attribute map to avoid concurrency issue.
- Map<String, DynamicMetric> newAttributeMap = new HashMap<>();
-
- // Get all attributes that can be emitted by the dynamicMetrics.
- for (DynamicMetric<?, ?> dynamicMetric : dynamicMetrics) {
- for (MBeanAttributeInfo attributeInfo : dynamicMetric.getAttributeInfos()) {
- // Info list to create MBean info
- attributeInfoList.add(attributeInfo);
- // Attribute mapping for getting attribute value when getAttribute() is called
- newAttributeMap.put(attributeInfo.getName(), dynamicMetric);
+ if (dynamicMetrics != null) {
+ for (DynamicMetric dynamicMetric : dynamicMetrics) {
+ Iterator<MBeanAttributeInfo> iter = dynamicMetric.getAttributeInfos().iterator();
+ while (iter.hasNext()) {
+ MBeanAttributeInfo attributeInfo = iter.next();
+ // Info list to create MBean info
+ attributeInfoList.add(attributeInfo);
+ // Attribute mapping for getting attribute value when getAttribute() is called
+ _attributeMap.put(attributeInfo.getName(), dynamicMetric);
+ }
}
}
@@ -130,19 +130,17 @@ public abstract class DynamicMBeanProvider implements DynamicMBean, SensorNamePr
String.format("Default %s Constructor", getClass().getSimpleName()),
getClass().getConstructors()[0]);
- MBeanAttributeInfo[] attributesInfo = new MBeanAttributeInfo[attributeInfoList.size()];
- attributesInfo = attributeInfoList.toArray(attributesInfo);
+ MBeanAttributeInfo[] attributeInfos = new MBeanAttributeInfo[attributeInfoList.size()];
+ attributeInfos = attributeInfoList.toArray(attributeInfos);
if (description == null) {
description = DEFAULT_DESCRIPTION;
}
- _mBeanInfo = new MBeanInfo(getClass().getName(), description, attributesInfo,
- new MBeanConstructorInfo[]{constructorInfo}, new MBeanOperationInfo[0],
- new MBeanNotificationInfo[0]);
-
- // Update _attributeMap reference.
- _attributeMap = newAttributeMap;
+ _mBeanInfo = new MBeanInfo(getClass().getName(), description, attributeInfos,
+ new MBeanConstructorInfo[] {
+ constructorInfo
+ }, new MBeanOperationInfo[0], new MBeanNotificationInfo[0]);
}
/**
@@ -160,17 +158,17 @@ public abstract class DynamicMBeanProvider implements DynamicMBean, SensorNamePr
}
@Override
- public Object getAttribute(String attribute) throws AttributeNotFoundException {
+ public Object getAttribute(String attribute)
+ throws AttributeNotFoundException, MBeanException, ReflectionException {
if (SENSOR_NAME_TAG.equals(attribute)) {
return getSensorName();
}
- DynamicMetric metric = _attributeMap.get(attribute);
- if (metric == null) {
- throw new AttributeNotFoundException("Attribute[" + attribute + "] is not found.");
+ if (!_attributeMap.containsKey(attribute)) {
+ return null;
}
- return metric.getAttributeValue(attribute);
+ return _attributeMap.get(attribute).getAttributeValue(attribute);
}
@Override
@@ -180,7 +178,7 @@ public abstract class DynamicMBeanProvider implements DynamicMBean, SensorNamePr
try {
Object value = getAttribute(attributeName);
attributeList.add(new Attribute(attributeName, value));
- } catch (AttributeNotFoundException ex) {
+ } catch (AttributeNotFoundException | MBeanException | ReflectionException ex) {
_logger.error("Failed to get attribute: " + attributeName, ex);
}
}
@@ -193,7 +191,8 @@ public abstract class DynamicMBeanProvider implements DynamicMBean, SensorNamePr
}
@Override
- public void setAttribute(Attribute attribute) {
+ public void setAttribute(Attribute attribute) throws AttributeNotFoundException,
+ InvalidAttributeValueException, MBeanException, ReflectionException {
// All MBeans are readonly
return;
}
@@ -205,7 +204,8 @@ public abstract class DynamicMBeanProvider implements DynamicMBean, SensorNamePr
}
@Override
- public Object invoke(String actionName, Object[] params, String[] signature) {
+ public Object invoke(String actionName, Object[] params, String[] signature)
+ throws MBeanException, ReflectionException {
// No operation supported
return null;
}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/SimpleDynamicMetric.java b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/SimpleDynamicMetric.java
index 2b0f1db..1be6a21 100644
--- a/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/SimpleDynamicMetric.java
+++ b/helix-core/src/main/java/org/apache/helix/monitoring/mbeans/dynamicMBeans/SimpleDynamicMetric.java
@@ -25,7 +25,7 @@ package org.apache.helix.monitoring.mbeans.dynamicMBeans;
* @param <T> the type of the metric value
*/
public class SimpleDynamicMetric<T> extends DynamicMetric<T, T> {
- protected final String _metricName;
+ private final String _metricName;
/**
* Instantiates a new Simple dynamic metric.
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/MetricCollector.java b/helix-core/src/main/java/org/apache/helix/monitoring/metrics/MetricCollector.java
deleted file mode 100644
index b08a840..0000000
--- a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/MetricCollector.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package org.apache.helix.monitoring.metrics;
-
-/*
- * 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.
- */
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import javax.management.JMException;
-import javax.management.ObjectName;
-import org.apache.helix.HelixException;
-import org.apache.helix.monitoring.metrics.model.Metric;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMBeanProvider;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMetric;
-
-/**
- * Collects and manages all metrics that implement the {@link Metric} interface.
- */
-public abstract class MetricCollector extends DynamicMBeanProvider {
- private static final String CLUSTER_NAME_KEY = "ClusterName";
- private static final String ENTITY_NAME_KEY = "EntityName";
- private final String _monitorDomainName;
- private final String _clusterName;
- private final String _entityName;
- private Map<String, Metric> _metricMap;
-
- public MetricCollector(String monitorDomainName, String clusterName, String entityName) {
- _monitorDomainName = monitorDomainName;
- _clusterName = clusterName;
- _entityName = entityName;
- _metricMap = new HashMap<>();
- }
-
- @Override
- public DynamicMBeanProvider register() throws JMException {
- // First cast all Metric objects to DynamicMetrics
- Collection<DynamicMetric<?, ?>> dynamicMetrics = new HashSet<>();
- _metricMap.values().forEach(metric -> dynamicMetrics.add(metric.getDynamicMetric()));
-
- // Define MBeanName and ObjectName
- // MBean name has two key-value pairs:
- // ------ 1) ClusterName KV pair (first %s=%s)
- // ------ 2) EntityName KV pair (second %s=%s)
- String mbeanName =
- String.format("%s=%s, %s=%s", CLUSTER_NAME_KEY, _clusterName, ENTITY_NAME_KEY, _entityName);
-
- // ObjectName has one key-value pair:
- // ------ 1) Monitor domain name KV pair where value is the MBean name
- doRegister(dynamicMetrics,
- new ObjectName(String.format("%s:%s", _monitorDomainName, mbeanName)));
- return this;
- }
-
- @Override
- public String getSensorName() {
- return String.format("%s.%s.%s", _monitorDomainName, _clusterName,
- _entityName);
- }
-
- void addMetric(Metric metric) {
- if (metric instanceof DynamicMetric) {
- _metricMap.putIfAbsent(metric.getMetricName(), metric);
- } else {
- throw new HelixException("MetricCollector only supports Metrics that are DynamicMetric!");
- }
- }
-
- /**
- * Returns a desired type of the metric.
- * @param metricName
- * @param metricClass Desired type
- * @param <T> Casted result of the metric
- * @return
- */
- public <T extends DynamicMetric> T getMetric(String metricName, Class<T> metricClass) {
- return metricClass.cast(_metricMap.get(metricName));
- }
-
- public Map<String, Metric> getMetricMap() {
- return _metricMap;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/WagedRebalancerMetricCollector.java b/helix-core/src/main/java/org/apache/helix/monitoring/metrics/WagedRebalancerMetricCollector.java
deleted file mode 100644
index df8b60f..0000000
--- a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/WagedRebalancerMetricCollector.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package org.apache.helix.monitoring.metrics;
-
-/*
- * 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.
- */
-
-import javax.management.JMException;
-
-import org.apache.helix.HelixException;
-import org.apache.helix.monitoring.mbeans.MonitorDomainNames;
-import org.apache.helix.monitoring.metrics.implementation.BaselineDivergenceGauge;
-import org.apache.helix.monitoring.metrics.implementation.RebalanceCounter;
-import org.apache.helix.monitoring.metrics.implementation.RebalanceFailureCount;
-import org.apache.helix.monitoring.metrics.implementation.RebalanceLatencyGauge;
-import org.apache.helix.monitoring.metrics.model.CountMetric;
-import org.apache.helix.monitoring.metrics.model.LatencyMetric;
-import org.apache.helix.monitoring.metrics.model.RatioMetric;
-
-
-public class WagedRebalancerMetricCollector extends MetricCollector {
- private static final String WAGED_REBALANCER_ENTITY_NAME = "WagedRebalancer";
-
- /**
- * This enum class contains all metric names defined for WagedRebalancer. Note that all enums are
- * in camel case for readability.
- */
- public enum WagedRebalancerMetricNames {
- // Per-stage latency metrics
- GlobalBaselineCalcLatencyGauge,
- PartialRebalanceLatencyGauge,
-
- // The following latency metrics are related to AssignmentMetadataStore
- StateReadLatencyGauge,
- StateWriteLatencyGauge,
-
- /*
- * Gauge of the difference (state and partition allocation) between the baseline and the best
- * possible assignment.
- */
- BaselineDivergenceGauge,
-
- // Count of any rebalance compute failure.
- // Note the rebalancer may still be able to return the last known-good assignment on a rebalance
- // compute failure. And this fallback logic won't impact this counting.
- RebalanceFailureCounter,
-
- // Waged rebalance counters.
- GlobalBaselineCalcCounter,
- PartialRebalanceCounter
- }
-
- public WagedRebalancerMetricCollector(String clusterName) {
- super(MonitorDomainNames.Rebalancer.name(), clusterName, WAGED_REBALANCER_ENTITY_NAME);
- createMetrics();
- if (clusterName != null) {
- try {
- register();
- } catch (JMException e) {
- throw new HelixException("Failed to register MBean for the WagedRebalancerMetricCollector.",
- e);
- }
- }
- }
-
- /**
- * This constructor will create but will not register metrics. This constructor will be used in
- * case of JMException so that the rebalancer could proceed without registering and emitting
- * metrics.
- */
- public WagedRebalancerMetricCollector() {
- this(null);
- }
-
- /**
- * Creates and registers all metrics in MetricCollector for WagedRebalancer.
- */
- private void createMetrics() {
- // Define all metrics
- LatencyMetric globalBaselineCalcLatencyGauge =
- new RebalanceLatencyGauge(WagedRebalancerMetricNames.GlobalBaselineCalcLatencyGauge.name(),
- getResetIntervalInMs());
- LatencyMetric partialRebalanceLatencyGauge =
- new RebalanceLatencyGauge(WagedRebalancerMetricNames.PartialRebalanceLatencyGauge.name(),
- getResetIntervalInMs());
- LatencyMetric stateReadLatencyGauge =
- new RebalanceLatencyGauge(WagedRebalancerMetricNames.StateReadLatencyGauge.name(),
- getResetIntervalInMs());
- LatencyMetric stateWriteLatencyGauge =
- new RebalanceLatencyGauge(WagedRebalancerMetricNames.StateWriteLatencyGauge.name(),
- getResetIntervalInMs());
- RatioMetric baselineDivergenceGauge =
- new BaselineDivergenceGauge(WagedRebalancerMetricNames.BaselineDivergenceGauge.name());
- CountMetric calcFailureCount =
- new RebalanceFailureCount(WagedRebalancerMetricNames.RebalanceFailureCounter.name());
- CountMetric globalBaselineCalcCounter =
- new RebalanceCounter(WagedRebalancerMetricNames.GlobalBaselineCalcCounter.name());
- CountMetric partialRebalanceCounter =
- new RebalanceCounter(WagedRebalancerMetricNames.PartialRebalanceCounter.name());
-
- // Add metrics to WagedRebalancerMetricCollector
- addMetric(globalBaselineCalcLatencyGauge);
- addMetric(partialRebalanceLatencyGauge);
- addMetric(stateReadLatencyGauge);
- addMetric(stateWriteLatencyGauge);
- addMetric(baselineDivergenceGauge);
- addMetric(calcFailureCount);
- addMetric(globalBaselineCalcCounter);
- addMetric(partialRebalanceCounter);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/BaselineDivergenceGauge.java b/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/BaselineDivergenceGauge.java
deleted file mode 100644
index 8e6d49b..0000000
--- a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/BaselineDivergenceGauge.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package org.apache.helix.monitoring.metrics.implementation;
-
-/*
- * 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.
- */
-
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
-
-import org.apache.helix.controller.pipeline.AbstractBaseStage;
-import org.apache.helix.controller.rebalancer.util.ResourceUsageCalculator;
-import org.apache.helix.model.ResourceAssignment;
-import org.apache.helix.monitoring.metrics.model.RatioMetric;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-/**
- * Gauge of the difference (state and partition allocation) between the baseline and the best
- * possible assignment. Its value range is [0.0, 1.0].
- */
-public class BaselineDivergenceGauge extends RatioMetric {
- private static final Logger LOG = LoggerFactory.getLogger(BaselineDivergenceGauge.class);
-
- /**
- * Instantiates a new Simple dynamic metric.
- * @param metricName the metric name
- */
- public BaselineDivergenceGauge(String metricName) {
- super(metricName, 0.0d);
- }
-
- /**
- * Asynchronously measure and update metric value.
- * @param threadPool an executor service to asynchronously run the task
- * @param baseline baseline assignment
- * @param bestPossibleAssignment best possible assignment
- */
- public void asyncMeasureAndUpdateValue(ExecutorService threadPool,
- Map<String, ResourceAssignment> baseline,
- Map<String, ResourceAssignment> bestPossibleAssignment) {
- AbstractBaseStage.asyncExecute(threadPool, () -> {
- try {
- double baselineDivergence =
- ResourceUsageCalculator.measureBaselineDivergence(baseline, bestPossibleAssignment);
- updateValue(baselineDivergence);
- } catch (Exception e) {
- LOG.error("Failed to report BaselineDivergenceGauge metric.", e);
- }
- return null;
- });
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/RebalanceCounter.java b/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/RebalanceCounter.java
deleted file mode 100644
index 8ecce7c..0000000
--- a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/RebalanceCounter.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package org.apache.helix.monitoring.metrics.implementation;
-
-/*
- * 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.
- */
-
-import org.apache.helix.monitoring.metrics.model.CountMetric;
-
-
-/**
- * To report counter type metrics related to rebalance. This monitor monotonically increases values.
- */
-public class RebalanceCounter extends CountMetric {
- /**
- * Instantiates a new rebalance count metric.
- * @param metricName the metric name
- */
- public RebalanceCounter(String metricName) {
- super(metricName, 0L);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/RebalanceFailureCount.java b/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/RebalanceFailureCount.java
deleted file mode 100644
index fd335f2..0000000
--- a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/RebalanceFailureCount.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.apache.helix.monitoring.metrics.implementation;
-
-/*
- * 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.
- */
-
-import org.apache.helix.monitoring.metrics.model.CountMetric;
-
-
-public class RebalanceFailureCount extends CountMetric {
- /**
- * Instantiates a new Simple dynamic metric.
- *
- * @param metricName the metric name
- */
- public RebalanceFailureCount(String metricName) {
- super(metricName, 0L);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/RebalanceLatencyGauge.java b/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/RebalanceLatencyGauge.java
deleted file mode 100644
index b0c563b..0000000
--- a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/implementation/RebalanceLatencyGauge.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package org.apache.helix.monitoring.metrics.implementation;
-
-/*
- * 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.
- */
-
-import java.util.concurrent.TimeUnit;
-
-import com.codahale.metrics.Histogram;
-import com.codahale.metrics.SlidingTimeWindowArrayReservoir;
-import org.apache.helix.monitoring.metrics.model.LatencyMetric;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class RebalanceLatencyGauge extends LatencyMetric {
- private static final Logger LOG = LoggerFactory.getLogger(RebalanceLatencyGauge.class);
- private static final long VALUE_NOT_SET = -1;
- private long _lastEmittedMetricValue = VALUE_NOT_SET;
- // Use threadlocal here so the start time can be updated and recorded in multi-threads.
- private final ThreadLocal<Long> _startTime;
-
- /**
- * Instantiates a new Histogram dynamic metric.
- * @param metricName the metric name
- */
- public RebalanceLatencyGauge(String metricName, long slidingTimeWindow) {
- super(metricName, new Histogram(
- new SlidingTimeWindowArrayReservoir(slidingTimeWindow, TimeUnit.MILLISECONDS)));
- _metricName = metricName;
- _startTime = ThreadLocal.withInitial(() -> VALUE_NOT_SET);
- }
-
- /**
- * Calling this method multiple times would simply overwrite the previous state. This is because
- * the rebalancer could fail at any point, and we want it to recover gracefully by resetting the
- * internal state of this metric.
- */
- @Override
- public void startMeasuringLatency() {
- reset();
- _startTime.set(System.currentTimeMillis());
- }
-
- @Override
- public void endMeasuringLatency() {
- if (_startTime.get() == VALUE_NOT_SET) {
- LOG.error(
- "Needs to call startMeasuringLatency first! Ignoring and resetting the metric. Metric name: {}",
- _metricName);
- return;
- }
- synchronized (this) {
- _lastEmittedMetricValue = System.currentTimeMillis() - _startTime.get();
- updateValue(_lastEmittedMetricValue);
- }
- reset();
- }
-
- /**
- * Returns the most recently emitted metric value at the time of the call.
- * @return
- */
- @Override
- public Long getLastEmittedMetricValue() {
- return _lastEmittedMetricValue;
- }
-
- /**
- * Resets the internal state of this metric.
- */
- private void reset() {
- _startTime.set(VALUE_NOT_SET);
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/CountMetric.java b/helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/CountMetric.java
deleted file mode 100644
index c64f761..0000000
--- a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/CountMetric.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package org.apache.helix.monitoring.metrics.model;
-
-/*
- * 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.
- */
-
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMetric;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.SimpleDynamicMetric;
-
-/**
- * Represents a count metric and defines methods to help with calculation. A count metric gives a
- * gauge value of a certain property.
- */
-public abstract class CountMetric extends SimpleDynamicMetric<Long> implements Metric<Long> {
-
- /**
- * Instantiates a new count metric.
- *
- * @param metricName the metric name
- * @param initCount the initial count
- */
- public CountMetric(String metricName, long initCount) {
- super(metricName, initCount);
- }
-
- /**
- * Increment the metric by the input count.
- *
- * @param count
- */
- public void increment(long count) {
- updateValue(getValue() + count);
- }
-
- @Override
- public String getMetricName() {
- return _metricName;
- }
-
- @Override
- public String toString() {
- return String.format("Metric %s's count is %d", getMetricName(), getValue());
- }
-
- @Override
- public Long getLastEmittedMetricValue() {
- return getValue();
- }
-
- @Override
- public DynamicMetric getDynamicMetric() {
- return this;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/LatencyMetric.java b/helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/LatencyMetric.java
deleted file mode 100644
index 733635e..0000000
--- a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/LatencyMetric.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.apache.helix.monitoring.metrics.model;
-
-/*
- * 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.
- */
-
-import com.codahale.metrics.Histogram;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMetric;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.HistogramDynamicMetric;
-
-/**
- * Represents a latency metric and defines methods to help with calculation. A latency metric gives
- * how long a particular stage in the logic took in milliseconds.
- */
-public abstract class LatencyMetric extends HistogramDynamicMetric implements Metric<Long> {
- protected String _metricName;
-
- /**
- * Instantiates a new Histogram dynamic metric.
- * @param metricName the metric name
- * @param metricObject the metric object
- */
- public LatencyMetric(String metricName, Histogram metricObject) {
- super(metricName, metricObject);
- _metricName = metricName;
- }
-
- /**
- * Starts measuring the latency.
- */
- public abstract void startMeasuringLatency();
-
- /**
- * Ends measuring the latency.
- */
- public abstract void endMeasuringLatency();
-
- @Override
- public String getMetricName() {
- return _metricName;
- }
-
- @Override
- public String toString() {
- return String.format("Metric %s's latency is %d", getMetricName(), getLastEmittedMetricValue());
- }
-
- @Override
- public DynamicMetric getDynamicMetric() {
- return this;
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/RatioMetric.java b/helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/RatioMetric.java
deleted file mode 100644
index d321e51..0000000
--- a/helix-core/src/main/java/org/apache/helix/monitoring/metrics/model/RatioMetric.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.apache.helix.monitoring.metrics.model;
-
-/*
- * 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.
- */
-
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.DynamicMetric;
-import org.apache.helix.monitoring.mbeans.dynamicMBeans.SimpleDynamicMetric;
-
-
-/**
- * A gauge which defines the ratio of one value to another.
- */
-public abstract class RatioMetric extends SimpleDynamicMetric<Double> implements Metric<Double> {
- /**
- * Instantiates a new Simple dynamic metric.
- * @param metricName the metric name
- * @param metricObject the metric object
- */
- public RatioMetric(String metricName, double metricObject) {
- super(metricName, metricObject);
- }
-
- @Override
- public DynamicMetric getDynamicMetric() {
- return this;
- }
-
- @Override
- public String getMetricName() {
- return _metricName;
- }
-
- @Override
- public Double getLastEmittedMetricValue() {
- return getValue();
- }
-
- @Override
- public String toString() {
- return String.format("Metric name: %s, metric value: %f", getMetricName(), getValue());
- }
-}
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java
index 66143fe..d190976 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/BestPossibleExternalViewVerifier.java
@@ -27,37 +27,27 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import org.apache.helix.HelixDefinedState;
-import org.apache.helix.HelixRebalanceException;
import org.apache.helix.PropertyKey;
import org.apache.helix.controller.common.PartitionStateMap;
import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
import org.apache.helix.controller.pipeline.Stage;
import org.apache.helix.controller.pipeline.StageContext;
-import org.apache.helix.controller.rebalancer.waged.AssignmentMetadataStore;
-import org.apache.helix.controller.rebalancer.waged.RebalanceAlgorithm;
-import org.apache.helix.controller.rebalancer.waged.WagedRebalancer;
-import org.apache.helix.controller.rebalancer.waged.constraints.ConstraintBasedAlgorithmFactory;
import org.apache.helix.controller.stages.AttributeName;
import org.apache.helix.controller.stages.BestPossibleStateCalcStage;
import org.apache.helix.controller.stages.BestPossibleStateOutput;
import org.apache.helix.controller.stages.ClusterEvent;
import org.apache.helix.controller.stages.ClusterEventType;
import org.apache.helix.controller.stages.CurrentStateComputationStage;
-import org.apache.helix.controller.stages.CurrentStateOutput;
import org.apache.helix.controller.stages.ResourceComputationStage;
-import org.apache.helix.manager.zk.ZkBucketDataAccessor;
import org.apache.helix.manager.zk.ZkClient;
import org.apache.helix.manager.zk.client.HelixZkClient;
-import org.apache.helix.model.ClusterConfig;
import org.apache.helix.model.ExternalView;
import org.apache.helix.model.IdealState;
import org.apache.helix.model.Partition;
import org.apache.helix.model.Resource;
-import org.apache.helix.model.ResourceAssignment;
import org.apache.helix.model.StateModelDefinition;
import org.apache.helix.task.TaskConstants;
import org.slf4j.Logger;
@@ -387,16 +377,8 @@ public class BestPossibleExternalViewVerifier extends ZkHelixClusterVerifier {
}
runStage(event, new CurrentStateComputationStage());
- // Note the dryrunWagedRebalancer is just for one time usage
- DryrunWagedRebalancer dryrunWagedRebalancer =
- new DryrunWagedRebalancer(_zkClient.getServers(), cache.getClusterName(),
- cache.getClusterConfig().getGlobalRebalancePreference());
- event.addAttribute(AttributeName.STATEFUL_REBALANCER.name(), dryrunWagedRebalancer);
- try {
- runStage(event, new BestPossibleStateCalcStage());
- } finally {
- dryrunWagedRebalancer.close();
- }
+ // TODO: be caution here, should be handled statelessly.
+ runStage(event, new BestPossibleStateCalcStage());
BestPossibleStateOutput output = event.getAttribute(AttributeName.BEST_POSSIBLE_STATE.name());
return output;
@@ -416,55 +398,4 @@ public class BestPossibleExternalViewVerifier extends ZkHelixClusterVerifier {
return verifierName + "(" + _clusterName + "@" + _zkClient + "@resources["
+ (_resources != null ? Arrays.toString(_resources.toArray()) : "") + "])";
}
-
- /**
- * A Dryrun WAGED rebalancer that only calculates the assignment based on the cluster status but
- * never update the rebalancer assignment metadata.
- * This rebalacer is used in the verifiers or tests.
- */
- private class DryrunWagedRebalancer extends WagedRebalancer {
- DryrunWagedRebalancer(String metadataStoreAddrs, String clusterName,
- Map<ClusterConfig.GlobalRebalancePreferenceKey, Integer> preferences) {
- super(new ReadOnlyAssignmentMetadataStore(metadataStoreAddrs, clusterName),
- ConstraintBasedAlgorithmFactory.getInstance(preferences), Optional.empty());
- }
-
- @Override
- protected Map<String, ResourceAssignment> computeBestPossibleAssignment(
- ResourceControllerDataProvider clusterData, Map<String, Resource> resourceMap,
- Set<String> activeNodes, CurrentStateOutput currentStateOutput, RebalanceAlgorithm algorithm)
- throws HelixRebalanceException {
- return getBestPossibleAssignment(getAssignmentMetadataStore(), currentStateOutput,
- resourceMap.keySet());
- }
- }
-
- private class ReadOnlyAssignmentMetadataStore extends AssignmentMetadataStore {
- ReadOnlyAssignmentMetadataStore(String metadataStoreAddrs, String clusterName) {
- super(new ZkBucketDataAccessor(metadataStoreAddrs), clusterName);
- }
-
- @Override
- public boolean persistBaseline(Map<String, ResourceAssignment> globalBaseline) {
- // If baseline hasn't changed, skip writing to metadata store
- if (compareAssignments(_globalBaseline, globalBaseline)) {
- return false;
- }
- // Update the in-memory reference only
- _globalBaseline = globalBaseline;
- return true;
- }
-
- @Override
- public boolean persistBestPossibleAssignment(
- Map<String, ResourceAssignment> bestPossibleAssignment) {
- // If bestPossibleAssignment hasn't changed, skip writing to metadata store
- if (compareAssignments(_bestPossibleAssignment, bestPossibleAssignment)) {
- return false;
- }
- // Update the in-memory reference only
- _bestPossibleAssignment = bestPossibleAssignment;
- return true;
- }
- }
}
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java
index 0b3c97e..13cc260 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/StrictMatchExternalViewVerifier.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -55,34 +56,19 @@ public class StrictMatchExternalViewVerifier extends ZkHelixClusterVerifier {
private final Set<String> _resources;
private final Set<String> _expectLiveInstances;
- private final boolean _isDeactivatedNodeAware;
- @Deprecated
public StrictMatchExternalViewVerifier(String zkAddr, String clusterName, Set<String> resources,
Set<String> expectLiveInstances) {
- this(zkAddr, clusterName, resources, expectLiveInstances, false);
- }
-
- @Deprecated
- public StrictMatchExternalViewVerifier(HelixZkClient zkClient, String clusterName,
- Set<String> resources, Set<String> expectLiveInstances) {
- this(zkClient, clusterName, resources, expectLiveInstances, false);
- }
-
- private StrictMatchExternalViewVerifier(String zkAddr, String clusterName, Set<String> resources,
- Set<String> expectLiveInstances, boolean isDeactivatedNodeAware) {
super(zkAddr, clusterName);
_resources = resources;
_expectLiveInstances = expectLiveInstances;
- _isDeactivatedNodeAware = isDeactivatedNodeAware;
}
- private StrictMatchExternalViewVerifier(HelixZkClient zkClient, String clusterName,
- Set<String> resources, Set<String> expectLiveInstances, boolean isDeactivatedNodeAware) {
+ public StrictMatchExternalViewVerifier(HelixZkClient zkClient, String clusterName,
+ Set<String> resources, Set<String> expectLiveInstances) {
super(zkClient, clusterName);
_resources = resources;
_expectLiveInstances = expectLiveInstances;
- _isDeactivatedNodeAware = isDeactivatedNodeAware;
}
public static class Builder {
@@ -91,8 +77,6 @@ public class StrictMatchExternalViewVerifier extends ZkHelixClusterVerifier {
private Set<String> _expectLiveInstances;
private String _zkAddr;
private HelixZkClient _zkClient;
- // For backward compatibility, set the default isDeactivatedNodeAware to be false.
- private boolean _isDeactivatedNodeAware = false;
public StrictMatchExternalViewVerifier build() {
if (_clusterName == null || (_zkAddr == null && _zkClient == null)) {
@@ -101,10 +85,10 @@ public class StrictMatchExternalViewVerifier extends ZkHelixClusterVerifier {
if (_zkClient != null) {
return new StrictMatchExternalViewVerifier(_zkClient, _clusterName, _resources,
- _expectLiveInstances, _isDeactivatedNodeAware);
+ _expectLiveInstances);
}
return new StrictMatchExternalViewVerifier(_zkAddr, _clusterName, _resources,
- _expectLiveInstances, _isDeactivatedNodeAware);
+ _expectLiveInstances);
}
public Builder(String clusterName) {
@@ -155,15 +139,6 @@ public class StrictMatchExternalViewVerifier extends ZkHelixClusterVerifier {
_zkClient = zkClient;
return this;
}
-
- public boolean getDeactivatedNodeAwareness() {
- return _isDeactivatedNodeAware;
- }
-
- public Builder setDeactivatedNodeAwareness(boolean isDeactivatedNodeAware) {
- _isDeactivatedNodeAware = isDeactivatedNodeAware;
- return this;
- }
}
@Override
@@ -303,21 +278,17 @@ public class StrictMatchExternalViewVerifier extends ZkHelixClusterVerifier {
String stateModelDefName = idealState.getStateModelDefRef();
StateModelDefinition stateModelDef = cache.getStateModelDef(stateModelDefName);
- Map<String, Map<String, String>> idealPartitionState = new HashMap<>();
+ Map<String, Map<String, String>> idealPartitionState =
+ new HashMap<String, Map<String, String>>();
+
+ Set<String> liveEnabledInstances = new HashSet<String>(cache.getLiveInstances().keySet());
+ liveEnabledInstances.removeAll(cache.getDisabledInstances());
for (String partition : idealState.getPartitionSet()) {
List<String> preferenceList = AbstractRebalancer
- .getPreferenceList(new Partition(partition), idealState, cache.getEnabledLiveInstances());
- Map<String, String> idealMapping;
- if (_isDeactivatedNodeAware) {
- idealMapping = HelixUtil
- .computeIdealMapping(preferenceList, stateModelDef, cache.getLiveInstances().keySet(),
- cache.getDisabledInstancesForPartition(idealState.getResourceName(), partition));
- } else {
- idealMapping = HelixUtil
- .computeIdealMapping(preferenceList, stateModelDef, cache.getEnabledLiveInstances(),
- Collections.emptySet());
- }
+ .getPreferenceList(new Partition(partition), idealState, liveEnabledInstances);
+ Map<String, String> idealMapping =
+ HelixUtil.computeIdealMapping(preferenceList, stateModelDef, liveEnabledInstances);
idealPartitionState.put(partition, idealMapping);
}
diff --git a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java
index 6efdff5..020acbc 100644
--- a/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java
+++ b/helix-core/src/main/java/org/apache/helix/tools/ClusterVerifiers/ZkHelixClusterVerifier.java
@@ -20,6 +20,7 @@ package org.apache.helix.tools.ClusterVerifiers;
*/
import java.util.List;
+import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -36,6 +37,7 @@ import org.apache.helix.manager.zk.ZkBaseDataAccessor;
import org.apache.helix.manager.zk.ZkClient;
import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
import org.apache.helix.manager.zk.client.HelixZkClient;
+import org.apache.helix.model.ResourceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -166,6 +168,10 @@ public abstract class ZkHelixClusterVerifier
long start = System.currentTimeMillis();
boolean success;
do {
+ // Add a rebalance invoker in case some callbacks got buried - sometimes callbacks get
+ // processed even before changes get fully written to ZK.
+ invokeRebalance(_accessor);
+
success = verifyState();
if (success) {
return true;
@@ -301,4 +307,15 @@ public abstract class ZkHelixClusterVerifier
public String getClusterName() {
return _clusterName;
}
+
+ /**
+ * Invoke a cluster rebalance in case some callbacks get ignored. This is for Helix integration
+ * testing purposes only.
+ */
+ public static synchronized void invokeRebalance(HelixDataAccessor accessor) {
+ String dummyName = UUID.randomUUID().toString();
+ ResourceConfig dummyConfig = new ResourceConfig(dummyName);
+ accessor.updateProperty(accessor.keyBuilder().resourceConfig(dummyName), dummyConfig);
+ accessor.removeProperty(accessor.keyBuilder().resourceConfig(dummyName));
+ }
}
diff --git a/helix-core/src/main/java/org/apache/helix/util/HelixUtil.java b/helix-core/src/main/java/org/apache/helix/util/HelixUtil.java
index bfda60e..a31c3fe 100644
--- a/helix-core/src/main/java/org/apache/helix/util/HelixUtil.java
+++ b/helix-core/src/main/java/org/apache/helix/util/HelixUtil.java
@@ -201,42 +201,23 @@ public final class HelixUtil {
*/
public static Map<String, String> computeIdealMapping(List<String> preferenceList,
StateModelDefinition stateModelDef, Set<String> liveAndEnabled) {
- return computeIdealMapping(preferenceList, stateModelDef, liveAndEnabled,
- Collections.emptySet());
- }
-
- /**
- * compute the ideal mapping for resource in Full-Auto and Semi-Auto based on its preference list
- */
- public static Map<String, String> computeIdealMapping(List<String> preferenceList,
- StateModelDefinition stateModelDef, Set<String> liveInstanceSet,
- Set<String> disabledInstancesForPartition) {
Map<String, String> idealStateMap = new HashMap<String, String>();
if (preferenceList == null) {
return idealStateMap;
}
- for (String instance : preferenceList) {
- if (disabledInstancesForPartition.contains(instance) && liveInstanceSet.contains(instance)) {
- idealStateMap.put(instance, stateModelDef.getInitialState());
- }
- }
-
- Set<String> liveAndEnabledInstances = new HashSet<>(liveInstanceSet);
- liveAndEnabledInstances.removeAll(disabledInstancesForPartition);
-
List<String> statesPriorityList = stateModelDef.getStatesPriorityList();
Set<String> assigned = new HashSet<String>();
for (String state : statesPriorityList) {
- int stateCount = AbstractRebalancer
- .getStateCount(state, stateModelDef, liveAndEnabledInstances.size(), preferenceList.size());
+ int stateCount = AbstractRebalancer.getStateCount(state, stateModelDef, liveAndEnabled.size(),
+ preferenceList.size());
for (String instance : preferenceList) {
if (stateCount <= 0) {
break;
}
- if (!assigned.contains(instance) && liveAndEnabledInstances.contains(instance)) {
+ if (!assigned.contains(instance)) {
idealStateMap.put(instance, state);
assigned.add(instance);
stateCount--;
diff --git a/helix-core/src/main/java/org/apache/helix/util/RebalanceUtil.java b/helix-core/src/main/java/org/apache/helix/util/RebalanceUtil.java
index 050762d..18163bd 100644
--- a/helix-core/src/main/java/org/apache/helix/util/RebalanceUtil.java
+++ b/helix-core/src/main/java/org/apache/helix/util/RebalanceUtil.java
@@ -143,11 +143,6 @@ public class RebalanceUtil {
}
public static void scheduleOnDemandPipeline(String clusterName, long delay) {
- scheduleOnDemandPipeline(clusterName, delay, true);
- }
-
- public static void scheduleOnDemandPipeline(String clusterName, long delay,
- boolean shouldRefreshCache) {
if (clusterName == null) {
LOG.error("Failed to issue a pipeline run. ClusterName is null.");
return;
@@ -158,7 +153,7 @@ public class RebalanceUtil {
}
GenericHelixController controller = GenericHelixController.getController(clusterName);
if (controller != null) {
- controller.scheduleOnDemandRebalance(delay, shouldRefreshCache);
+ controller.scheduleOnDemandRebalance(delay);
} else {
LOG.error("Failed to issue a pipeline. Controller for cluster {} does not exist.",
clusterName);
diff --git a/helix-core/src/main/resources/soft-constraint-weight.properties b/helix-core/src/main/resources/soft-constraint-weight.properties
deleted file mode 100644
index c3c7931..0000000
--- a/helix-core/src/main/resources/soft-constraint-weight.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# 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.
-#
-
-# Define the constraint weights for the WAGED rebalancer in this file.
-#
-# PartitionMovementConstraint=1f
-# InstancePartitionsCountConstraint=0.15f
-# ResourcePartitionAntiAffinityConstraint=0.05f
-# ResourceTopStateAntiAffinityConstraint=0.3f
-# MaxCapacityUsageInstanceConstraint=0.6f
diff --git a/helix-core/src/test/java/org/apache/helix/common/ZkTestBase.java b/helix-core/src/test/java/org/apache/helix/common/ZkTestBase.java
index 61c2544..50c36ee 100644
--- a/helix-core/src/test/java/org/apache/helix/common/ZkTestBase.java
+++ b/helix-core/src/test/java/org/apache/helix/common/ZkTestBase.java
@@ -53,7 +53,6 @@ import org.apache.helix.controller.pipeline.Stage;
import org.apache.helix.controller.pipeline.StageContext;
import org.apache.helix.controller.rebalancer.DelayedAutoRebalancer;
import org.apache.helix.controller.rebalancer.strategy.AutoRebalanceStrategy;
-import org.apache.helix.controller.rebalancer.waged.WagedRebalancer;
import org.apache.helix.controller.stages.AttributeName;
import org.apache.helix.controller.stages.ClusterEvent;
import org.apache.helix.manager.zk.ZKHelixAdmin;
@@ -348,19 +347,6 @@ public class ZkTestBase {
protected IdealState createResourceWithDelayedRebalance(String clusterName, String db,
String stateModel, int numPartition, int replica, int minActiveReplica, long delay,
String rebalanceStrategy) {
- return createResource(clusterName, db, stateModel, numPartition, replica, minActiveReplica,
- delay, DelayedAutoRebalancer.class.getName(), rebalanceStrategy);
- }
-
- protected IdealState createResourceWithWagedRebalance(String clusterName, String db,
- String stateModel, int numPartition, int replica, int minActiveReplica) {
- return createResource(clusterName, db, stateModel, numPartition, replica, minActiveReplica,
- -1, WagedRebalancer.class.getName(), null);
- }
-
- private IdealState createResource(String clusterName, String db, String stateModel,
- int numPartition, int replica, int minActiveReplica, long delay, String rebalancerClassName,
- String rebalanceStrategy) {
IdealState idealState =
_gSetupTool.getClusterManagementTool().getResourceIdealState(clusterName, db);
if (idealState == null) {
@@ -376,7 +362,7 @@ public class ZkTestBase {
if (delay > 0) {
idealState.setRebalanceDelay(delay);
}
- idealState.setRebalancerClassName(rebalancerClassName);
+ idealState.setRebalancerClassName(DelayedAutoRebalancer.class.getName());
_gSetupTool.getClusterManagementTool().setResourceIdealState(clusterName, db, idealState);
_gSetupTool.rebalanceStorageCluster(clusterName, db, replica);
idealState = _gSetupTool.getClusterManagementTool().getResourceIdealState(clusterName, db);
diff --git a/helix-core/src/test/java/org/apache/helix/controller/changedetector/TestResourceChangeDetector.java b/helix-core/src/test/java/org/apache/helix/controller/changedetector/TestResourceChangeDetector.java
deleted file mode 100644
index bac9842..0000000
--- a/helix-core/src/test/java/org/apache/helix/controller/changedetector/TestResourceChangeDetector.java
+++ /dev/null
@@ -1,441 +0,0 @@
-package org.apache.helix.controller.changedetector;
-
-/*
- * 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.
- */
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-
-import org.apache.helix.AccessOption;
-import org.apache.helix.HelixConstants.ChangeType;
-import org.apache.helix.HelixDataAccessor;
-import org.apache.helix.PropertyKey;
-import org.apache.helix.TestHelper;
-import org.apache.helix.common.ZkTestBase;
-import org.apache.helix.controller.dataproviders.ResourceControllerDataProvider;
-import org.apache.helix.integration.manager.ClusterControllerManager;
-import org.apache.helix.integration.manager.MockParticipantManager;
-import org.apache.helix.manager.zk.ZKHelixDataAccessor;
-import org.apache.helix.model.ClusterConfig;
-import org.apache.helix.model.IdealState;
-import org.apache.helix.model.InstanceConfig;
-import org.apache.helix.model.ResourceConfig;
-import org.testng.Assert;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-/**
- * This test contains a series of unit tests for ResourceChangeDetector.
- */
-public class TestResourceChangeDetector extends ZkTestBase {
-
- // All possible change types for ResourceChangeDetector except for ClusterConfig
- // since we don't provide the names of changed fields for ClusterConfig
- private static final ChangeType[] RESOURCE_CHANGE_TYPES = {
- ChangeType.IDEAL_STATE, ChangeType.INSTANCE_CONFIG, ChangeType.LIVE_INSTANCE,
- ChangeType.RESOURCE_CONFIG, ChangeType.CLUSTER_CONFIG
- };
-
- private static final String CLUSTER_NAME = TestHelper.getTestClassName();
- private static final String RESOURCE_NAME = "TestDB";
- private static final String NEW_RESOURCE_NAME = "TestDB2";
- private static final String STATE_MODEL = "MasterSlave";
- // There are 5 possible change types for ResourceChangeDetector
- private static final int NUM_CHANGE_TYPES = 5;
- private static final int NUM_RESOURCES = 1;
- private static final int NUM_PARTITIONS = 10;
- private static final int NUM_REPLICAS = 3;
- private static final int NUM_NODES = 5;
-
- // Create a mock of ResourceControllerDataProvider so that we could manipulate it
- private ResourceControllerDataProvider _dataProvider;
- private ResourceChangeDetector _resourceChangeDetector;
- private ClusterControllerManager _controller;
- private MockParticipantManager[] _participants = new MockParticipantManager[NUM_NODES];
- private HelixDataAccessor _dataAccessor;
- private PropertyKey.Builder _keyBuilder;
-
- @BeforeClass
- public void beforeClass() throws Exception {
- super.beforeClass();
-
- // Set up a mock cluster
- TestHelper.setupCluster(CLUSTER_NAME, ZK_ADDR, 12918, // participant port
- "localhost", // participant name prefix
- RESOURCE_NAME, // resource name prefix
- NUM_RESOURCES, // resources
- NUM_PARTITIONS, // partitions per resource
- NUM_NODES, // nodes
- NUM_REPLICAS, // replicas
- STATE_MODEL, true); // do rebalance
-
- // Start a controller
- _controller = new ClusterControllerManager(ZK_ADDR, CLUSTER_NAME, "controller_0");
- _controller.syncStart();
-
- // Start Participants
- for (int i = 0; i < NUM_NODES; i++) {
- String instanceName = "localhost_" + (12918 + i);
- _participants[i] = new MockParticipantManager(ZK_ADDR, CLUSTER_NAME, instanceName);
- _participants[i].syncStart();
- }
-
- _dataAccessor = new ZKHelixDataAccessor(CLUSTER_NAME, _baseAccessor);
- _keyBuilder = _dataAccessor.keyBuilder();
- _resourceChangeDetector = new ResourceChangeDetector();
-
- // Create a custom data provider
- _dataProvider = new ResourceControllerDataProvider(CLUSTER_NAME);
- }
-
- @AfterClass
- public void afterClass() throws Exception {
- for (MockParticipantManager participant : _participants) {
- if (participant != null && participant.isConnected()) {
- participant.syncStop();
- }
- }
- _controller.syncStop();
- deleteCluster(CLUSTER_NAME);
- Assert.assertFalse(TestHelper.verify(() -> _dataAccessor.getBaseDataAccessor()
- .exists("/" + CLUSTER_NAME, AccessOption.PERSISTENT), 20000L));
- }
-
- /**
- * Tests the initialization of the change detector. It should tell us that there's been changes
- * for every change type and for all items per type.
- * @throws Exception
- */
- @Test
- public void testResourceChangeDetectorInit() {
- _dataProvider.refresh(_dataAccessor);
- _resourceChangeDetector.updateSnapshots(_dataProvider);
-
- Collection<ChangeType> changeTypes = _resourceChangeDetector.getChangeTypes();
- Assert.assertEquals(changeTypes.size(), NUM_CHANGE_TYPES,
- "Not all change types have been detected for ResourceChangeDetector!");
-
- // Check that the right amount of resources show up as added
- checkDetectionCounts(ChangeType.IDEAL_STATE, NUM_RESOURCES, 0, 0);
-
- // Check that the right amount of instances show up as added
- checkDetectionCounts(ChangeType.LIVE_INSTANCE, NUM_NODES, 0, 0);
- checkDetectionCounts(ChangeType.INSTANCE_CONFIG, NUM_NODES, 0, 0);
-
- // Check that the right amount of cluster config item show up
- checkDetectionCounts(ChangeType.CLUSTER_CONFIG, 1, 0, 0);
- }
-
- /**
- * Add a resource (IS and ResourceConfig) and see if the detector detects it.
- */
- @Test(dependsOnMethods = "testResourceChangeDetectorInit")
- public void testAddResource() {
- // Create an IS and ResourceConfig
- _gSetupTool.getClusterManagementTool().addResource(CLUSTER_NAME, NEW_RESOURCE_NAME,
- NUM_PARTITIONS, STATE_MODEL);
- ResourceConfig resourceConfig = new ResourceConfig(NEW_RESOURCE_NAME);
- _dataAccessor.setProperty(_keyBuilder.resourceConfig(NEW_RESOURCE_NAME), resourceConfig);
- // Manually notify dataProvider
- _dataProvider.notifyDataChange(ChangeType.IDEAL_STATE);
- _dataProvider.notifyDataChange(ChangeType.RESOURCE_CONFIG);
-
- // Refresh the data provider
- _dataProvider.refresh(_dataAccessor);
-
- // Update the detector
- _resourceChangeDetector.updateSnapshots(_dataProvider);
-
- checkChangeTypes(ChangeType.IDEAL_STATE, ChangeType.RESOURCE_CONFIG);
- // Check the counts
- for (ChangeType type : RESOURCE_CHANGE_TYPES) {
- if (type == ChangeType.IDEAL_STATE || type == ChangeType.RESOURCE_CONFIG) {
- checkDetectionCounts(type, 1, 0, 0);
- } else {
- checkDetectionCounts(type, 0, 0, 0);
- }
- }
- // Check that detector gives the right item
- Assert.assertTrue(_resourceChangeDetector.getAdditionsByType(ChangeType.RESOURCE_CONFIG)
- .contains(NEW_RESOURCE_NAME));
- }
-
- /**
- * Modify a resource config for the new resource and test that detector detects it.
- */
- @Test(dependsOnMethods = "testAddResource")
- public void testModifyResource() {
- // Modify resource config
- ResourceConfig resourceConfig =
- _dataAccessor.getProperty(_keyBuilder.resourceConfig(NEW_RESOURCE_NAME));
- resourceConfig.getRecord().setSimpleField("Did I change?", "Yes!");
- _dataAccessor.updateProperty(_keyBuilder.resourceConfig(NEW_RESOURCE_NAME), resourceConfig);
-
- // Notify data provider and check
- _dataProvider.notifyDataChange(ChangeType.RESOURCE_CONFIG);
- _dataProvider.refresh(_dataAccessor);
- _resourceChangeDetector.updateSnapshots(_dataProvider);
-
- checkChangeTypes(ChangeType.RESOURCE_CONFIG);
- // Check the counts
- for (ChangeType type : RESOURCE_CHANGE_TYPES) {
- if (type == ChangeType.RESOURCE_CONFIG) {
- checkDetectionCounts(type, 0, 1, 0);
- } else {
- checkDetectionCounts(type, 0, 0, 0);
- }
- }
- Assert.assertTrue(_resourceChangeDetector.getChangesByType(ChangeType.RESOURCE_CONFIG)
- .contains(NEW_RESOURCE_NAME));
- }
-
- /**
- * Delete the new resource and test that detector detects it.
- */
- @Test(dependsOnMethods = "testModifyResource")
- public void testDeleteResource() {
- // Delete the newly added resource
- _dataAccessor.removeProperty(_keyBuilder.idealStates(NEW_RESOURCE_NAME));
- _dataAccessor.removeProperty(_keyBuilder.resourceConfig(NEW_RESOURCE_NAME));
-
- // Notify data provider and check
- _dataProvider.notifyDataChange(ChangeType.IDEAL_STATE);
- _dataProvider.notifyDataChange(ChangeType.RESOURCE_CONFIG);
- _dataProvider.refresh(_dataAccessor);
- _resourceChangeDetector.updateSnapshots(_dataProvider);
-
- checkChangeTypes(ChangeType.RESOURCE_CONFIG, ChangeType.IDEAL_STATE);
- // Check the counts
- for (ChangeType type : RESOURCE_CHANGE_TYPES) {
- if (type == ChangeType.IDEAL_STATE || type == ChangeType.RESOURCE_CONFIG) {
- checkDetectionCounts(type, 0, 0, 1);
- } else {
- checkDetectionCounts(type, 0, 0, 0);
- }
- }
- }
-
- /**
- * Disconnect and reconnect a Participant and see if detector detects.
- */
- @Test(dependsOnMethods = "testDeleteResource")
- public void testDisconnectReconnectInstance() {
- // Disconnect a Participant
- _participants[0].syncStop();
- _dataProvider.notifyDataChange(ChangeType.LIVE_INSTANCE);
- _dataProvider.refresh(_dataAccessor);
- _resourceChangeDetector.updateSnapshots(_dataProvider);
-
- checkChangeTypes(ChangeType.LIVE_INSTANCE);
- // Check the counts
- for (ChangeType type : RESOURCE_CHANGE_TYPES) {
- if (type == ChangeType.LIVE_INSTANCE) {
- checkDetectionCounts(type, 0, 0, 1);
- } else {
- checkDetectionCounts(type, 0, 0, 0);
- }
- }
-
- // Reconnect the Participant
- _participants[0] = new MockParticipantManager(ZK_ADDR, CLUSTER_NAME, "localhost_12918");
- _participants[0].syncStart();
- _dataProvider.notifyDataChange(ChangeType.LIVE_INSTANCE);
- _dataProvider.refresh(_dataAccessor);
- _resourceChangeDetector.updateSnapshots(_dataProvider);
-
- checkChangeTypes(ChangeType.LIVE_INSTANCE);
- // Check the counts
- for (ChangeType type : RESOURCE_CHANGE_TYPES) {
- if (type == ChangeType.LIVE_INSTANCE) {
- checkDetectionCounts(type, 1, 0, 0);
- } else {
- checkDetectionCounts(type, 0, 0, 0);
- }
- }
- }
-
- /**
- * Remove an instance completely and see if detector detects.
- */
- @Test(dependsOnMethods = "testDisconnectReconnectInstance")
- public void testRemoveInstance() {
- _participants[0].syncStop();
- InstanceConfig instanceConfig =
- _dataAccessor.getProperty(_keyBuilder.instanceConfig(_participants[0].getInstanceName()));
- _gSetupTool.getClusterManagementTool().dropInstance(CLUSTER_NAME, instanceConfig);
-
- _dataProvider.notifyDataChange(ChangeType.LIVE_INSTANCE);
... 9360 lines suppressed ...
|