jiajunwang commented on a change in pull request #1935: URL: https://github.com/apache/helix/pull/1935#discussion_r789028024
########## File path: helix-core/src/main/java/org/apache/helix/cloud/topology/VirtualTopologyGroupStrategy.java ########## @@ -0,0 +1,80 @@ +package org.apache.helix.cloud.topology; + +/* + * 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.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.helix.util.HelixUtil; + +public enum VirtualTopologyGroupStrategy { + + /** + * A default assignment strategy that is deterministic and stable where: + * 1. assignment is guaranteed consistent for same inputs. + * 2. number of instance shuffles is reduced. + */ + DEFAULT { + @Override + public Map<String, Set<String>> computeAssignment( + int numGroups, String virtualGroupName, Map<String, Set<String>> zoneMapping) { + List<String> sortedInstances = HelixUtil.sortAndFlattenZoneMapping(zoneMapping); + Map<String, Set<String>> assignment = new HashMap<>(); + int instancesPerGroup = sortedInstances.size() / numGroups; + + // instances.size = instancePerGroup * numGroups + residual + // step 1, continuously assign following groupInd order until entering residual section + for (int instanceInd = 0; instanceInd < instancesPerGroup * numGroups; instanceInd++) { + int groupIndex = instanceInd / instancesPerGroup; + String groupId = computeVirtualGroupId(groupIndex, virtualGroupName); + assignment.putIfAbsent(groupId, new HashSet<>()); + assignment.get(groupId).add(sortedInstances.get(instanceInd)); + } + // step 2, assign the residuals, either stick to the last assigned group or round-robin to all groups. + for (int instanceInd = instancesPerGroup * numGroups; instanceInd < sortedInstances.size(); instanceInd++) { + int groupIndex = numGroups <= zoneMapping.keySet().size() + ? numGroups - 1 + : instanceInd % numGroups; + String groupId = computeVirtualGroupId(groupIndex, virtualGroupName); + assignment.putIfAbsent(groupId, new HashSet<>()); + assignment.get(groupId).add(sortedInstances.get(instanceInd)); + } + return ImmutableMap.copyOf(assignment); + } + }; + + /** + * Compute the assignment for each virtual topology group. + * + * @param numGroups number of the virtual groups + * @param virtualGroupName virtual group name + * @param zoneMapping current zone mapping from zoneId to instanceIds + * @return the assignment as mapping from virtual group ID to instanceIds + */ + public abstract Map<String, Set<String>> computeAssignment( + int numGroups, String virtualGroupName, Map<String, Set<String>> zoneMapping); + + private static String computeVirtualGroupId(int groupIndex, String virtualGroupName) { + return virtualGroupName + "_" + groupIndex; Review comment: I can see you did > private static final String DOMAIN_FIELD_SPLITTER = ","; > private static final String DOMAIN_VALUE_JOINER = "="; for the InstanceConfig. Then why hardcode here? It's better to keep the same style. ########## File path: helix-core/src/main/java/org/apache/helix/cloud/topology/VirtualTopologyGroupStrategy.java ########## @@ -0,0 +1,80 @@ +package org.apache.helix.cloud.topology; + +/* + * 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.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.helix.util.HelixUtil; + +public enum VirtualTopologyGroupStrategy { Review comment: I agree strategy fits well. But due to the existing usage, I would say, "arrangement" or "scheme" for this class? ########## File path: helix-rest/src/main/java/org/apache/helix/rest/server/service/VirtualTopologyGroupService.java ########## @@ -0,0 +1,163 @@ +package org.apache.helix.rest.server.service; + +/* + * 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.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import org.apache.helix.AccessOption; +import org.apache.helix.ConfigAccessor; +import org.apache.helix.HelixAdmin; +import org.apache.helix.HelixDataAccessor; +import org.apache.helix.HelixException; +import org.apache.helix.common.VirtualTopologyGroupConstants; +import org.apache.helix.cloud.topology.VirtualTopologyGroupStrategy; +import org.apache.helix.model.CloudConfig; +import org.apache.helix.model.ClusterConfig; +import org.apache.helix.model.HelixConfigScope; +import org.apache.helix.model.InstanceConfig; +import org.apache.helix.model.builder.HelixConfigScopeBuilder; +import org.apache.helix.rest.server.json.cluster.ClusterTopology; +import org.apache.helix.zookeeper.datamodel.ZNRecord; +import org.apache.helix.zookeeper.zkclient.DataUpdater; + + +/** + * Service for virtual topology group. + */ +public class VirtualTopologyGroupService { + private final HelixAdmin _helixAdmin; + private final ClusterService _clusterService; + private final ConfigAccessor _configAccessor; + private final HelixDataAccessor _dataAccessor; + private final VirtualTopologyGroupStrategy _strategy; + + public VirtualTopologyGroupService(HelixAdmin helixAdmin, ClusterService clusterService, + ConfigAccessor configAccessor, HelixDataAccessor dataAccessor) { + _helixAdmin = helixAdmin; + _clusterService = clusterService; + _configAccessor = configAccessor; + _dataAccessor = dataAccessor; + _strategy = VirtualTopologyGroupStrategy.DEFAULT; + } + + /** + * Add virtual topology group for a cluster. + * This includes calculating the virtual group assignment for all instances in the cluster then update instance config + * and cluster config. Cluster is expected to enter maintenanceMode during config update, this is either enabled/disabled + * in place this method or handled by client side code. + * @param clusterName the cluster name. + * @param customFields custom fields, {@link VirtualTopologyGroupConstants#GROUP_NAME} + * and {@link VirtualTopologyGroupConstants#GROUP_NUMBER} are required + * @param enterMaintenanceMode if enabled, the cluster will enter maintenance mode during the setup and exit once it + * completes. Otherwise, it's expected the maintenanceMode is controlled by client side. + */ + public void addVirtualTopologyGroup(String clusterName, Map<String, String> customFields, boolean enterMaintenanceMode) { + // only support if CLOUD_ENABLED AND VIRTUAL_GROUP_ENABLED + CloudConfig cloudConfig = _configAccessor.getCloudConfig(clusterName); + if (cloudConfig == null || !cloudConfig.isCloudEnabled()) { + throw new HelixException( + "Cloud is not enabled, addVirtualTopologyGroup is not allowed to run in non-cloud environment."); Review comment: As we discussed offline, the cloud here does not necessarily mean a real cloud platform. It solely means that Helix gets topology information from the participant under this scenario. Meaning the topology information is automatically provided instead of manually populated. So this restriction makes sense here. ########## File path: helix-rest/src/main/java/org/apache/helix/rest/server/service/VirtualTopologyGroupService.java ########## @@ -0,0 +1,163 @@ +package org.apache.helix.rest.server.service; + +/* + * 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.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import org.apache.helix.AccessOption; +import org.apache.helix.ConfigAccessor; +import org.apache.helix.HelixAdmin; +import org.apache.helix.HelixDataAccessor; +import org.apache.helix.HelixException; +import org.apache.helix.common.VirtualTopologyGroupConstants; +import org.apache.helix.cloud.topology.VirtualTopologyGroupStrategy; +import org.apache.helix.model.CloudConfig; +import org.apache.helix.model.ClusterConfig; +import org.apache.helix.model.HelixConfigScope; +import org.apache.helix.model.InstanceConfig; +import org.apache.helix.model.builder.HelixConfigScopeBuilder; +import org.apache.helix.rest.server.json.cluster.ClusterTopology; +import org.apache.helix.zookeeper.datamodel.ZNRecord; +import org.apache.helix.zookeeper.zkclient.DataUpdater; + + +/** + * Service for virtual topology group. + */ +public class VirtualTopologyGroupService { + private final HelixAdmin _helixAdmin; + private final ClusterService _clusterService; + private final ConfigAccessor _configAccessor; + private final HelixDataAccessor _dataAccessor; + private final VirtualTopologyGroupStrategy _strategy; + + public VirtualTopologyGroupService(HelixAdmin helixAdmin, ClusterService clusterService, + ConfigAccessor configAccessor, HelixDataAccessor dataAccessor) { + _helixAdmin = helixAdmin; + _clusterService = clusterService; + _configAccessor = configAccessor; + _dataAccessor = dataAccessor; + _strategy = VirtualTopologyGroupStrategy.DEFAULT; + } + + /** + * Add virtual topology group for a cluster. + * This includes calculating the virtual group assignment for all instances in the cluster then update instance config + * and cluster config. Cluster is expected to enter maintenanceMode during config update, this is either enabled/disabled + * in place this method or handled by client side code. + * @param clusterName the cluster name. + * @param customFields custom fields, {@link VirtualTopologyGroupConstants#GROUP_NAME} + * and {@link VirtualTopologyGroupConstants#GROUP_NUMBER} are required + * @param enterMaintenanceMode if enabled, the cluster will enter maintenance mode during the setup and exit once it + * completes. Otherwise, it's expected the maintenanceMode is controlled by client side. + */ + public void addVirtualTopologyGroup(String clusterName, Map<String, String> customFields, boolean enterMaintenanceMode) { + // only support if CLOUD_ENABLED AND VIRTUAL_GROUP_ENABLED + CloudConfig cloudConfig = _configAccessor.getCloudConfig(clusterName); + if (cloudConfig == null || !cloudConfig.isCloudEnabled()) { + throw new HelixException( + "Cloud is not enabled, addVirtualTopologyGroup is not allowed to run in non-cloud environment."); + } + ClusterConfig clusterConfig = _configAccessor.getClusterConfig(clusterName); + if (!clusterConfig.isVirtualGroupEnabled()) { + throw new HelixException("Virtual Group is disabled in cluster " + clusterName); + } + // validation + String groupName = customFields.get(VirtualTopologyGroupConstants.GROUP_NAME); + String groupNumberStr = Preconditions.checkNotNull( + customFields.get(VirtualTopologyGroupConstants.GROUP_NUMBER), + "virtualTopologyGroupNumber cannot be empty!"); + Preconditions.checkState(!StringUtils.isEmpty(groupName), "virtualTopologyGroupName cannot be empty!"); + int numGroups = Integer.parseInt(groupNumberStr); Review comment: I was thinking about negative numbers or zero. But if the group number is larger than the node count, can we still assign it? It's also OK if you let the compute class throw exceptions. But please ensure the exception is understandable for our customers. IndexOutOfBoundsException is not acceptable. ########## File path: helix-core/src/main/java/org/apache/helix/cloud/topology/VirtualTopologyGroupStrategy.java ########## @@ -0,0 +1,80 @@ +package org.apache.helix.cloud.topology; + +/* + * 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.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.helix.util.HelixUtil; + +public enum VirtualTopologyGroupStrategy { + + /** + * A default assignment strategy that is deterministic and stable where: + * 1. assignment is guaranteed consistent for same inputs. + * 2. number of instance shuffles is reduced. + */ + DEFAULT { + @Override + public Map<String, Set<String>> computeAssignment( + int numGroups, String virtualGroupName, Map<String, Set<String>> zoneMapping) { + List<String> sortedInstances = HelixUtil.sortAndFlattenZoneMapping(zoneMapping); + Map<String, Set<String>> assignment = new HashMap<>(); + int instancesPerGroup = sortedInstances.size() / numGroups; Review comment: Not exactly, the reason you get the conclusion of 6 groups is that you sort the instance and compute groupIndex with "instanceInd / instancesPerGroup". My original idea is that we can directly use mod to get the index. However, according to what we have discussed yesterday, it might be problematic to assign instances of one physical zone to multiple virtual groups. It may effectively downgrade fault tolerance. So I assume you will have to change the design here. Let's discuss further when your new design is ready : ) ########## File path: helix-core/src/main/java/org/apache/helix/model/ClusterConfig.java ########## @@ -52,6 +52,7 @@ FAULT_ZONE_TYPE, // the type in which isolation should be applied on when Helix places the // replicas from same partition. TOPOLOGY_AWARE_ENABLED, // whether topology aware rebalance is enabled. + VIRTUAL_GROUP_ENABLED, // whether virtual topology group is enabled Review comment: As we discussed offline yesterday, it is a must as long as we still want to keep the physical topology somewhere and may want to provide a way to roll back. But logic-wise, it introduced more dependencies. For example, what if I enable virtual group without enabling topology awareness, does it even makes sense? What about when cloud features are not enabled? I can foresee many questions from our users. ########## File path: helix-rest/src/main/java/org/apache/helix/rest/server/service/VirtualTopologyGroupService.java ########## @@ -0,0 +1,163 @@ +package org.apache.helix.rest.server.service; + +/* + * 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.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.commons.lang3.StringUtils; +import org.apache.helix.AccessOption; +import org.apache.helix.ConfigAccessor; +import org.apache.helix.HelixAdmin; +import org.apache.helix.HelixDataAccessor; +import org.apache.helix.HelixException; +import org.apache.helix.common.VirtualTopologyGroupConstants; +import org.apache.helix.cloud.topology.VirtualTopologyGroupStrategy; +import org.apache.helix.model.CloudConfig; +import org.apache.helix.model.ClusterConfig; +import org.apache.helix.model.HelixConfigScope; +import org.apache.helix.model.InstanceConfig; +import org.apache.helix.model.builder.HelixConfigScopeBuilder; +import org.apache.helix.rest.server.json.cluster.ClusterTopology; +import org.apache.helix.zookeeper.datamodel.ZNRecord; +import org.apache.helix.zookeeper.zkclient.DataUpdater; + + +/** + * Service for virtual topology group. + */ +public class VirtualTopologyGroupService { + private final HelixAdmin _helixAdmin; + private final ClusterService _clusterService; + private final ConfigAccessor _configAccessor; + private final HelixDataAccessor _dataAccessor; + private final VirtualTopologyGroupStrategy _strategy; + + public VirtualTopologyGroupService(HelixAdmin helixAdmin, ClusterService clusterService, + ConfigAccessor configAccessor, HelixDataAccessor dataAccessor) { + _helixAdmin = helixAdmin; + _clusterService = clusterService; + _configAccessor = configAccessor; + _dataAccessor = dataAccessor; + _strategy = VirtualTopologyGroupStrategy.DEFAULT; + } + + /** + * Add virtual topology group for a cluster. + * This includes calculating the virtual group assignment for all instances in the cluster then update instance config + * and cluster config. Cluster is expected to enter maintenanceMode during config update, this is either enabled/disabled + * in place this method or handled by client side code. + * @param clusterName the cluster name. + * @param customFields custom fields, {@link VirtualTopologyGroupConstants#GROUP_NAME} + * and {@link VirtualTopologyGroupConstants#GROUP_NUMBER} are required + * @param enterMaintenanceMode if enabled, the cluster will enter maintenance mode during the setup and exit once it + * completes. Otherwise, it's expected the maintenanceMode is controlled by client side. + */ + public void addVirtualTopologyGroup(String clusterName, Map<String, String> customFields, boolean enterMaintenanceMode) { + // only support if CLOUD_ENABLED AND VIRTUAL_GROUP_ENABLED + CloudConfig cloudConfig = _configAccessor.getCloudConfig(clusterName); + if (cloudConfig == null || !cloudConfig.isCloudEnabled()) { + throw new HelixException( + "Cloud is not enabled, addVirtualTopologyGroup is not allowed to run in non-cloud environment."); + } + ClusterConfig clusterConfig = _configAccessor.getClusterConfig(clusterName); + if (!clusterConfig.isVirtualGroupEnabled()) { + throw new HelixException("Virtual Group is disabled in cluster " + clusterName); + } + // validation + String groupName = customFields.get(VirtualTopologyGroupConstants.GROUP_NAME); + String groupNumberStr = Preconditions.checkNotNull( + customFields.get(VirtualTopologyGroupConstants.GROUP_NUMBER), + "virtualTopologyGroupNumber cannot be empty!"); + Preconditions.checkState(!StringUtils.isEmpty(groupName), "virtualTopologyGroupName cannot be empty!"); + int numGroups = Integer.parseInt(groupNumberStr); + + // compute group assignment + ClusterTopology clusterTopology = _clusterService.getClusterTopology(clusterName); + Map<String, Set<String>> assignment = + _strategy.computeAssignment(numGroups, groupName, clusterTopology.toZoneMapping()); + + if (enterMaintenanceMode) { Review comment: > we shouldn't proceed if maintenance mode is already on? Yes, or you don't exit maintenance mode at the end. ########## File path: helix-core/src/main/java/org/apache/helix/common/VirtualTopologyGroupConstants.java ########## @@ -0,0 +1,31 @@ +package org.apache.helix.common; + +/* + * 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.cloud.azure.AzureConstants; + + +public class VirtualTopologyGroupConstants { + public static final String GROUP_NAME = "virtualTopologyGroupName"; + public static final String GROUP_NUMBER = "virtualTopologyGroupNumber"; + public static final String VIRTUAL_FAULT_ZONE_TYPE = "virtualZone"; + public static final String VIRTUAL_TOPOLOGY = new StringBuilder(AzureConstants.AZURE_TOPOLOGY) Review comment: I don't believe the high-level design of VirtualTopologyGroup is for Azure only. It should be determined by the caller when they call the Rest API. As we discussed yesterday, whenever a cluster is "cloud-enabled", it is supporting virtual topology. And for Helix, "cloud-enabled" does not mean a specific cloud platform or even public cloud. As long as the cluster nodes can provide the necessary information, it is supporting necessary cloud feature from Helix perspective. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
