Skip to content

Commit f11bc8a

Browse files
Incorported Review comments
1 parent 8b27bdd commit f11bc8a

File tree

3 files changed

+41
-50
lines changed

3 files changed

+41
-50
lines changed

helix-rest/src/main/java/org/apache/helix/rest/clusterMaintenanceService/StoppableInstancesSelector.java

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,16 @@ public class StoppableInstancesSelector {
6363
private final MaintenanceManagementService _maintenanceService;
6464
private final ClusterTopology _clusterTopology;
6565
private final ZKHelixDataAccessor _dataAccessor;
66-
private final boolean _preserveOrder;
6766

6867
private StoppableInstancesSelector(String clusterId, List<String> orderOfZone,
6968
String customizedInput, MaintenanceManagementService maintenanceService,
70-
ClusterTopology clusterTopology, ZKHelixDataAccessor dataAccessor,
71-
boolean preserveOrder) {
69+
ClusterTopology clusterTopology, ZKHelixDataAccessor dataAccessor) {
7270
_clusterId = clusterId;
7371
_orderOfZone = orderOfZone;
7472
_customizedInput = customizedInput;
7573
_maintenanceService = maintenanceService;
7674
_clusterTopology = clusterTopology;
7775
_dataAccessor = dataAccessor;
78-
_preserveOrder = preserveOrder;
7976
}
8077

8178
/**
@@ -86,14 +83,15 @@ private StoppableInstancesSelector(String clusterId, List<String> orderOfZone,
8683
*
8784
* @param instances A list of instance to be evaluated.
8885
* @param toBeStoppedInstances A list of instances presumed to be already stopped
86+
* @param preserveOrder Indicates whether to preserve the original order of instances
8987
* @return An ObjectNode containing:
9088
* - 'stoppableNode': List of instances that can be stopped.
9189
* - 'instance_not_stoppable_with_reasons': A map with the instance name as the key and
9290
* a list of getZoneBasedInstancesreasons for non-stoppability as the value.
9391
* @throws IOException
9492
*/
9593
public ObjectNode getStoppableInstancesInSingleZone(List<String> instances,
96-
List<String> toBeStoppedInstances) throws IOException {
94+
List<String> toBeStoppedInstances, boolean preserveOrder) throws IOException {
9795
ObjectNode result = JsonNodeFactory.instance.objectNode();
9896
ArrayNode stoppableInstances =
9997
result.putArray(InstancesAccessor.InstancesProperties.instance_stoppable_parallel.name());
@@ -102,9 +100,9 @@ public ObjectNode getStoppableInstancesInSingleZone(List<String> instances,
102100
Set<String> toBeStoppedInstancesSet = findToBeStoppedInstances(toBeStoppedInstances);
103101

104102
List<String> zoneBasedInstance =
105-
getZoneBasedInstances(instances, _clusterTopology.toZoneMapping());
103+
getZoneBasedInstances(instances, _clusterTopology.toZoneMapping(), preserveOrder);
106104
populateStoppableInstances(zoneBasedInstance, toBeStoppedInstancesSet, stoppableInstances,
107-
failedStoppableInstances, _preserveOrder);
105+
failedStoppableInstances, preserveOrder);
108106
processNonexistentInstances(instances, failedStoppableInstances);
109107

110108
return result;
@@ -117,6 +115,7 @@ public ObjectNode getStoppableInstancesInSingleZone(List<String> instances,
117115
*
118116
* @param instances A list of instance to be evaluated.
119117
* @param toBeStoppedInstances A list of instances presumed to be already stopped
118+
* @param preserveOrder Indicates whether to preserve the original order of instances
120119
* @return An ObjectNode containing:
121120
* - 'stoppableNode': List of instances that can be stopped.
122121
* - 'instance_not_stoppable_with_reasons': A map with the instance name as the key and
@@ -154,6 +153,7 @@ public ObjectNode getStoppableInstancesCrossZones(List<String> instances,
154153
*
155154
* @param instances A list of instance to be evaluated.
156155
* @param toBeStoppedInstances A list of instances presumed to be already stopped
156+
* @param preserveOrder Indicates whether to preserve the original order of instances
157157
* @return An ObjectNode containing:
158158
* - 'stoppableNode': List of instances that can be stopped.
159159
* - 'instance_not_stoppable_with_reasons': A map with the instance name as the key and
@@ -266,36 +266,37 @@ public void calculateOrderOfZone(List<String> instances, boolean random) {
266266
*
267267
* @param instances List of instances to be considered
268268
* @param zoneMapping Mapping from zone to instances
269+
* @param preserveOrder Indicates whether to preserve the original order of instances
269270
* @return List of instances in the first non-empty zone. If preserveOrder is true, the original order
270271
* of instances is maintained. If preserveOrder is false (default), instances are sorted lexicographically.
271272
*/
272273
private List<String> getZoneBasedInstances(List<String> instances,
273-
Map<String, Set<String>> zoneMapping) {
274+
Map<String, Set<String>> zoneMapping, boolean preserveOrder) {
274275
if (_orderOfZone.isEmpty()) {
275-
return _orderOfZone;
276+
return Collections.emptyList();
276277
}
277278

278-
Set<String> instanceSet = null;
279279
for (String zone : _orderOfZone) {
280-
if (_preserveOrder) {
281-
List<String> filteredInstances = new ArrayList<>(instances);
282-
Set<String> currentZoneInstanceSet = new HashSet<>(zoneMapping.get(zone));
283-
filteredInstances.removeIf(instance -> !currentZoneInstanceSet.contains(instance));
284-
if (!filteredInstances.isEmpty()) {
285-
return filteredInstances;
286-
}
287-
} else {
288-
// Original behavior - lexicographical ordering via TreeSet
289-
instanceSet = new TreeSet<>(instances);
290-
Set<String> currentZoneInstanceSet = new HashSet<>(zoneMapping.get(zone));
291-
instanceSet.retainAll(currentZoneInstanceSet);
292-
if (!instanceSet.isEmpty()) {
293-
return new ArrayList<>(instanceSet);
280+
Set<String> currentZoneInstanceSet = zoneMapping.get(zone);
281+
if (currentZoneInstanceSet == null || currentZoneInstanceSet.isEmpty()) {
282+
continue;
283+
}
284+
285+
// Filter instances based on current zone
286+
List<String> filteredInstances = instances.stream()
287+
.filter(currentZoneInstanceSet::contains)
288+
.collect(Collectors.toList());
289+
290+
if (!filteredInstances.isEmpty()) {
291+
// If preserve order is not required, return sorted list
292+
if (!preserveOrder) {
293+
Collections.sort(filteredInstances); // Lexicographical order
294294
}
295+
return filteredInstances;
295296
}
296297
}
297298

298-
return Collections.EMPTY_LIST;
299+
return Collections.emptyList();
299300
}
300301

301302
/**
@@ -343,7 +344,6 @@ public static class StoppableInstancesSelectorBuilder {
343344
private MaintenanceManagementService _maintenanceService;
344345
private ClusterTopology _clusterTopology;
345346
private ZKHelixDataAccessor _dataAccessor;
346-
private boolean _preserveOrder = false; // Default to false for backward compatibility
347347

348348
public StoppableInstancesSelectorBuilder setClusterId(String clusterId) {
349349
_clusterId = clusterId;
@@ -376,14 +376,9 @@ public StoppableInstancesSelectorBuilder setDataAccessor(ZKHelixDataAccessor dat
376376
return this;
377377
}
378378

379-
public StoppableInstancesSelectorBuilder setPreserveOrder(boolean preserveOrder) {
380-
_preserveOrder = preserveOrder;
381-
return this;
382-
}
383-
384379
public StoppableInstancesSelector build() {
385380
return new StoppableInstancesSelector(_clusterId, _orderOfZone, _customizedInput,
386-
_maintenanceService, _clusterTopology, _dataAccessor, _preserveOrder);
381+
_maintenanceService, _clusterTopology, _dataAccessor);
387382
}
388383
}
389384
}

helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/InstancesAccessor.java

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ public enum InstancesProperties {
7878
to_be_stopped_instances,
7979
skip_stoppable_check_list,
8080
customized_values,
81-
preserve_order,
8281
instance_stoppable_parallel,
8382
instance_not_stoppable_with_reasons
8483
}
@@ -160,7 +159,9 @@ public Response instancesOperations(@PathParam("clusterId") String clusterId,
160159
@QueryParam("continueOnFailures") boolean continueOnFailures,
161160
@QueryParam("skipZKRead") boolean skipZKRead,
162161
@QueryParam("skipHealthCheckCategories") String skipHealthCheckCategories,
163-
@DefaultValue("false") @QueryParam("random") boolean random, String content) {
162+
@DefaultValue("false") @QueryParam("random") boolean random,
163+
@DefaultValue("false") @QueryParam("preserveOrder") boolean preserveOrder,
164+
String content) {
164165
Command cmd;
165166
try {
166167
cmd = Command.valueOf(command);
@@ -205,7 +206,7 @@ public Response instancesOperations(@PathParam("clusterId") String clusterId,
205206
break;
206207
case stoppable:
207208
return batchGetStoppableInstances(clusterId, node, skipZKRead, continueOnFailures,
208-
skipHealthCheckCategorySet, random);
209+
skipHealthCheckCategorySet, random, preserveOrder);
209210
default:
210211
_logger.error("Unsupported command :" + command);
211212
return badRequest("Unsupported command :" + command);
@@ -223,7 +224,7 @@ public Response instancesOperations(@PathParam("clusterId") String clusterId,
223224

224225
private Response batchGetStoppableInstances(String clusterId, JsonNode node, boolean skipZKRead,
225226
boolean continueOnFailures, Set<StoppableCheck.Category> skipHealthCheckCategories,
226-
boolean random) throws IOException {
227+
boolean random, boolean preserveOrder) throws IOException {
227228
try {
228229
// TODO: Process input data from the content
229230
// TODO: Implement the logic to automatically detect the selection base. https://github.com/apache/helix/issues/2968#issue-2691677799
@@ -305,11 +306,6 @@ private Response batchGetStoppableInstances(String clusterId, JsonNode node, boo
305306
.asBoolean();
306307
}
307308

308-
boolean preserveOrder = false;
309-
if (node.get(InstancesProperties.preserve_order.name()) != null) {
310-
preserveOrder = node.get(InstancesProperties.preserve_order.name()).asBoolean();
311-
}
312-
313309
ClusterTopology clusterTopology = clusterService.getClusterTopology(clusterId);
314310
if (selectionBase != InstanceHealthSelectionBase.non_zone_based) {
315311
if (!clusterService.isClusterTopologyAware(clusterId)) {
@@ -360,14 +356,13 @@ private Response batchGetStoppableInstances(String clusterId, JsonNode node, boo
360356
.setMaintenanceService(maintenanceService)
361357
.setClusterTopology(clusterTopology)
362358
.setDataAccessor((ZKHelixDataAccessor) getDataAccssor(clusterId))
363-
.setPreserveOrder(preserveOrder)
364359
.build();
365360
ObjectNode result;
366361

367362
switch (selectionBase) {
368363
case zone_based:
369364
stoppableInstancesSelector.calculateOrderOfZone(instances, random);
370-
result = stoppableInstancesSelector.getStoppableInstancesInSingleZone(instances, toBeStoppedInstances);
365+
result = stoppableInstancesSelector.getStoppableInstancesInSingleZone(instances, toBeStoppedInstances, preserveOrder);
371366
break;
372367
case cross_zone_based:
373368
stoppableInstancesSelector.calculateOrderOfZone(instances, random);

helix-rest/src/test/java/org/apache/helix/rest/server/TestInstancesAccessor.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -659,15 +659,16 @@ public void testMultipleReplicasInSameMZWithPreserveOrder(boolean preserveOrder)
659659

660660
// Run stoppable check against the 3 instances where SemiAuto DB was assigned
661661
String content =
662-
String.format("{\"%s\":\"%s\",\"%s\":[\"%s\",\"%s\",\"%s\"],\"%s\":%s}",
662+
String.format("{\"%s\":\"%s\",\"%s\":[\"%s\",\"%s\",\"%s\"]}",
663663
InstancesAccessor.InstancesProperties.selection_base.name(),
664664
InstancesAccessor.InstanceHealthSelectionBase.zone_based.name(),
665-
InstancesAccessor.InstancesProperties.instances.name(), "instance1", "instance2", "instance0",
666-
InstancesAccessor.InstancesProperties.preserve_order.name(),
667-
preserveOrder);
668-
Response response =
669-
new JerseyUriRequestBuilder("clusters/{}/instances?command=stoppable&skipHealthCheckCategories=CUSTOM_INSTANCE_CHECK,CUSTOM_PARTITION_CHECK").format(
670-
STOPPABLE_CLUSTER2).post(this, Entity.entity(content, MediaType.APPLICATION_JSON_TYPE));
665+
InstancesAccessor.InstancesProperties.instances.name(), "instance1", "instance2", "instance0");
666+
Response response = new JerseyUriRequestBuilder(String.format(
667+
"clusters/%s/instances?command=stoppable&skipHealthCheckCategories=%s&preserveOrder=%s",
668+
STOPPABLE_CLUSTER2,
669+
"CUSTOM_INSTANCE_CHECK,CUSTOM_PARTITION_CHECK",
670+
preserveOrder))
671+
.post(this, Entity.entity(content, MediaType.APPLICATION_JSON_TYPE));
671672
JsonNode jsonNode = OBJECT_MAPPER.readTree(response.readEntity(String.class));
672673

673674
String stoppableNode = "instance0";

0 commit comments

Comments
 (0)