Skip to content

Commit 240772b

Browse files
jiajunwangJiajun Wang
authored andcommitted
Add cluster level default instance config. (#413)
This config will be applied to the instance when there is no (or empty) capacity configuration in the Instance Config. Also add unit tests.
1 parent 9f385bf commit 240772b

5 files changed

Lines changed: 134 additions & 5 deletions

File tree

helix-core/src/main/java/org/apache/helix/controller/rebalancer/waged/model/AssignableNode.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,15 @@ private void refresh(ClusterConfig clusterConfig, InstanceConfig instanceConfig,
8888
Collection<AssignableReplica> existingAssignment) {
8989
reset();
9090

91-
_currentCapacity.putAll(instanceConfig.getInstanceCapacityMap());
91+
Map<String, Integer> instanceCapacity = instanceConfig.getInstanceCapacityMap();
92+
if (instanceCapacity.isEmpty()) {
93+
instanceCapacity = clusterConfig.getDefaultInstanceCapacityMap();
94+
}
95+
_currentCapacity.putAll(instanceCapacity);
9296
_faultZone = computeFaultZone(clusterConfig, instanceConfig);
9397
_instanceTags = new HashSet<>(instanceConfig.getTags());
9498
_disabledPartitionsMap = instanceConfig.getDisabledPartitionsMap();
95-
_maxCapacity = instanceConfig.getInstanceCapacityMap();
99+
_maxCapacity = instanceCapacity;
96100
_maxPartition = clusterConfig.getMaxPartitionsPerInstance();
97101

98102
assignNewBatch(existingAssignment);

helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@
2929
import org.apache.helix.api.config.StateTransitionTimeoutConfig;
3030

3131
import java.util.ArrayList;
32+
import java.util.Collection;
3233
import java.util.Collections;
3334
import java.util.HashMap;
3435
import java.util.List;
3536
import java.util.Map;
37+
import java.util.stream.Collectors;
3638

3739
/**
3840
* Cluster configurations
@@ -86,6 +88,8 @@ public enum ClusterConfigProperty {
8688

8789
// The required instance capacity keys for resource partition assignment calculation.
8890
INSTANCE_CAPACITY_KEYS,
91+
// The default instance capacity if no capacity is configured in the Instance Config node.
92+
DEFAULT_INSTANCE_CAPACITY_MAP,
8993
// The preference of the rebalance result.
9094
// EVENNESS - Evenness of the resource utilization, partition, and top state distribution.
9195
// LESS_MOVEMENT - the tendency of keeping the current assignment instead of moving the partition for optimal assignment.
@@ -699,6 +703,53 @@ public List<String> getInstanceCapacityKeys() {
699703
return capacityKeys;
700704
}
701705

706+
/**
707+
* Get the default instance capacity information from the map fields.
708+
* @return data map if it exists, or empty map
709+
*/
710+
public Map<String, Integer> getDefaultInstanceCapacityMap() {
711+
Map<String, String> capacityData =
712+
_record.getMapField(ClusterConfigProperty.DEFAULT_INSTANCE_CAPACITY_MAP.name());
713+
714+
if (capacityData != null) {
715+
return capacityData.entrySet().stream().collect(
716+
Collectors.toMap(entry -> entry.getKey(), entry -> Integer.parseInt(entry.getValue())));
717+
}
718+
return Collections.emptyMap();
719+
}
720+
721+
/**
722+
* Set the default instance capacity information with an Integer mapping.
723+
* @param capacityDataMap - map of instance capacity data
724+
* @throws IllegalArgumentException - when any of the data value is a negative number or when the map is empty
725+
*
726+
* This information is required by the global rebalancer.
727+
* @see <a href="Rebalance Algorithm">
728+
* https://github.com/apache/helix/wiki/Design-Proposal---Weight-Aware-Globally-Even-Distribute-Rebalancer#rebalance-algorithm-adapter
729+
* </a>
730+
* If the instance capacity is not configured in neither Instance Config nor Cluster Config, the
731+
* cluster topology is considered invalid. So the rebalancer may stop working.
732+
*/
733+
public void setDefaultInstanceCapacityMap(Map<String, Integer> capacityDataMap)
734+
throws IllegalArgumentException {
735+
if (capacityDataMap == null || capacityDataMap.size() == 0) {
736+
throw new IllegalArgumentException("Default Instance Capacity Data is empty");
737+
}
738+
739+
Map<String, String> capacityData = new HashMap<>();
740+
741+
capacityDataMap.entrySet().stream().forEach(entry -> {
742+
if (entry.getValue() < 0) {
743+
throw new IllegalArgumentException(String
744+
.format("Default Instance Capacity Data contains a negative value: %s = %d",
745+
entry.getKey(), entry.getValue()));
746+
}
747+
capacityData.put(entry.getKey(), Integer.toString(entry.getValue()));
748+
});
749+
750+
_record.setMapField(ClusterConfigProperty.DEFAULT_INSTANCE_CAPACITY_MAP.name(), capacityData);
751+
}
752+
702753
/**
703754
* Set the global rebalancer's assignment preference.
704755
* @param preference A map of the GlobalRebalancePreferenceKey and the corresponding weight.

helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -507,8 +507,7 @@ public void setMaxConcurrentTask(int maxConcurrentTask) {
507507
}
508508

509509
/**
510-
* Get the instance capacity information from the map fields
511-
*
510+
* Get the instance capacity information from the map fields.
512511
* @return data map if it exists, or empty map
513512
*/
514513
public Map<String, Integer> getInstanceCapacityMap() {
@@ -523,9 +522,17 @@ public Map<String, Integer> getInstanceCapacityMap() {
523522
}
524523

525524
/**
526-
* Set the instance capacity information with an Integer mapping
525+
* Set the instance capacity information with an Integer mapping.
527526
* @param capacityDataMap - map of instance capacity data
528527
* @throws IllegalArgumentException - when any of the data value is a negative number or when the map is empty
528+
*
529+
* This information is required by the global rebalancer.
530+
* @see <a href="Rebalance Algorithm">
531+
* https://github.com/apache/helix/wiki/Design-Proposal---Weight-Aware-Globally-Even-Distribute-Rebalancer#rebalance-algorithm-adapter
532+
* </a>
533+
* If the instance capacity is not configured in neither Instance Config nor Cluster Config, the
534+
* cluster topology is considered invalid. So the rebalancer may stop working.
535+
* Note that when a rebalancer requires this capacity information, it will ignore INSTANCE_WEIGHT.
529536
*/
530537
public void setInstanceCapacityMap(Map<String, Integer> capacityDataMap)
531538
throws IllegalArgumentException {

helix-core/src/test/java/org/apache/helix/controller/rebalancer/waged/model/TestAssignableNode.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,17 @@ public void testParseFaultZone() throws IOException {
200200

201201
Assert.assertEquals(assignableNode.getFaultZone(), "2/testInstance/");
202202
}
203+
204+
@Test
205+
public void testDefaultInstanceCapacity() {
206+
ClusterConfig testClusterConfig = new ClusterConfig("testClusterConfigId");
207+
testClusterConfig.setDefaultInstanceCapacityMap(_capacityDataMap);
208+
209+
InstanceConfig testInstanceConfig = new InstanceConfig("testInstanceConfigId");
210+
211+
AssignableNode assignableNode =
212+
new AssignableNode(testClusterConfig, testInstanceConfig, _testInstanceId,
213+
Collections.emptyList());
214+
Assert.assertEquals(assignableNode.getMaxCapacity(), _capacityDataMap);
215+
}
203216
}

helix-core/src/test/java/org/apache/helix/model/TestClusterConfig.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
*/
2121

2222
import com.google.common.collect.ImmutableList;
23+
import com.google.common.collect.ImmutableMap;
24+
import org.apache.helix.ZNRecord;
2325
import org.testng.Assert;
2426
import org.testng.annotations.Test;
2527

@@ -127,4 +129,56 @@ public void testSetRebalancePreferenceInvalidNumber() {
127129
ClusterConfig testConfig = new ClusterConfig("testId");
128130
testConfig.setGlobalRebalancePreference(preference);
129131
}
132+
133+
@Test
134+
public void testGetInstanceCapacityMap() {
135+
Map<String, Integer> capacityDataMap = ImmutableMap.of("item1", 1, "item2", 2, "item3", 3);
136+
137+
Map<String, String> capacityDataMapString =
138+
ImmutableMap.of("item1", "1", "item2", "2", "item3", "3");
139+
140+
ZNRecord rec = new ZNRecord("testId");
141+
rec.setMapField(ClusterConfig.ClusterConfigProperty.DEFAULT_INSTANCE_CAPACITY_MAP.name(),
142+
capacityDataMapString);
143+
ClusterConfig testConfig = new ClusterConfig(rec);
144+
145+
Assert.assertTrue(testConfig.getDefaultInstanceCapacityMap().equals(capacityDataMap));
146+
}
147+
148+
@Test
149+
public void testGetInstanceCapacityMapEmpty() {
150+
ClusterConfig testConfig = new ClusterConfig("testId");
151+
152+
Assert.assertTrue(testConfig.getDefaultInstanceCapacityMap().equals(Collections.emptyMap()));
153+
}
154+
155+
@Test
156+
public void testSetInstanceCapacityMap() {
157+
Map<String, Integer> capacityDataMap = ImmutableMap.of("item1", 1, "item2", 2, "item3", 3);
158+
159+
Map<String, String> capacityDataMapString =
160+
ImmutableMap.of("item1", "1", "item2", "2", "item3", "3");
161+
162+
ClusterConfig testConfig = new ClusterConfig("testConfig");
163+
testConfig.setDefaultInstanceCapacityMap(capacityDataMap);
164+
165+
Assert.assertEquals(testConfig.getRecord().getMapField(ClusterConfig.ClusterConfigProperty.
166+
DEFAULT_INSTANCE_CAPACITY_MAP.name()), capacityDataMapString);
167+
}
168+
169+
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Default Instance Capacity Data is empty")
170+
public void testSetInstanceCapacityMapEmpty() {
171+
Map<String, Integer> capacityDataMap = new HashMap<>();
172+
173+
ClusterConfig testConfig = new ClusterConfig("testConfig");
174+
testConfig.setDefaultInstanceCapacityMap(capacityDataMap);
175+
}
176+
177+
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Default Instance Capacity Data contains a negative value: item3 = -3")
178+
public void testSetInstanceCapacityMapInvalid() {
179+
Map<String, Integer> capacityDataMap = ImmutableMap.of("item1", 1, "item2", 2, "item3", -3);
180+
181+
ClusterConfig testConfig = new ClusterConfig("testConfig");
182+
testConfig.setDefaultInstanceCapacityMap(capacityDataMap);
183+
}
130184
}

0 commit comments

Comments
 (0)