Search in sources :

Example 1 with HelixPropertyStore

use of org.apache.helix.store.HelixPropertyStore in project ambry by linkedin.

the class HelixBootstrapUpgradeUtil method addUpdateResources.

/**
 * Add and/or update resources in Helix based on the information in the static cluster map. This may involve adding
 * or removing partitions from under a resource, and adding or dropping resources altogether. This may also involve
 * changing the instance set for a partition under a resource, based on the static cluster map.
 * @param dcName the name of the datacenter being processed.
 * @param partitionsToInstancesInDc a map to be filled with the mapping of partitions to their instance sets in the
 *                                  given datacenter.
 */
private void addUpdateResources(String dcName, Map<String, Set<String>> partitionsToInstancesInDc) {
    HelixAdmin dcAdmin = adminForDc.get(dcName);
    List<String> resourcesInCluster = dcAdmin.getResourcesInCluster(clusterName);
    List<String> instancesWithDisabledPartition = new ArrayList<>();
    HelixPropertyStore<ZNRecord> helixPropertyStore = helixAdminOperation == HelixAdminOperation.DisablePartition ? createHelixPropertyStore(dcName) : null;
    // maxResource may vary from one dc to another (special partition class allows partitions to exist in one dc only)
    int maxResource = -1;
    for (String resourceName : resourcesInCluster) {
        boolean resourceModified = false;
        if (!resourceName.matches("\\d+")) {
            // cluster map. These will be ignored.
            continue;
        }
        maxResource = Math.max(maxResource, Integer.parseInt(resourceName));
        IdealState resourceIs = dcAdmin.getResourceIdealState(clusterName, resourceName);
        for (String partitionName : new HashSet<>(resourceIs.getPartitionSet())) {
            Set<String> instanceSetInHelix = resourceIs.getInstanceSet(partitionName);
            Set<String> instanceSetInStatic = partitionsToInstancesInDc.remove(partitionName);
            if (instanceSetInStatic == null || instanceSetInStatic.isEmpty()) {
                if (forceRemove) {
                    info("[{}] *** Partition {} no longer present in the static clustermap, {} *** ", dcName.toUpperCase(), partitionName, dryRun ? "no action as dry run" : "removing from Resource");
                    // Helix team is planning to provide an API for this.
                    if (!dryRun) {
                        resourceIs.getRecord().getListFields().remove(partitionName);
                    }
                    resourceModified = true;
                } else {
                    info("[{}] *** forceRemove option not provided, resources will not be removed (use --forceRemove to forcefully remove)", dcName.toUpperCase());
                    expectMoreInHelixDuringValidate = true;
                    partitionsNotForceRemovedByDc.computeIfAbsent(dcName, k -> ConcurrentHashMap.newKeySet()).add(partitionName);
                }
            } else if (!instanceSetInStatic.equals(instanceSetInHelix)) {
                // we change the IdealState only when the operation is meant to bootstrap cluster or indeed update IdealState
                if (EnumSet.of(HelixAdminOperation.UpdateIdealState, HelixAdminOperation.BootstrapCluster).contains(helixAdminOperation)) {
                    // @formatter:off
                    info("[{}] Different instance sets for partition {} under resource {}. {}. " + "Previous instance set: [{}], new instance set: [{}]", dcName.toUpperCase(), partitionName, resourceName, dryRun ? "No action as dry run" : "Updating Helix using static", String.join(",", instanceSetInHelix), String.join(",", instanceSetInStatic));
                    // @formatter:on
                    if (!dryRun) {
                        ArrayList<String> newInstances = new ArrayList<>(instanceSetInStatic);
                        Collections.shuffle(newInstances);
                        resourceIs.setPreferenceList(partitionName, newInstances);
                        // Existing resources may not have ANY_LIVEINSTANCE set as the numReplicas (which allows for different
                        // replication for different partitions under the same resource). So set it here (We use the name() method and
                        // not the toString() method for the enum as that is what Helix uses).
                        resourceIs.setReplicas(ResourceConfig.ResourceConfigConstants.ANY_LIVEINSTANCE.name());
                    }
                    resourceModified = true;
                } else if (helixAdminOperation == HelixAdminOperation.DisablePartition) {
                    // if this is DisablePartition operation, we don't modify IdealState and only make InstanceConfig to disable
                    // certain partition on specific node.
                    // 1. extract difference between Helix and Static instance sets. Determine which replica is removed
                    instanceSetInHelix.removeAll(instanceSetInStatic);
                    // 2. disable removed replica on certain node.
                    for (String instanceInHelixOnly : instanceSetInHelix) {
                        info("Partition {} under resource {} on node {} is no longer in static clustermap. {}.", partitionName, resourceName, instanceInHelixOnly, dryRun ? "No action as dry run" : "Disabling it");
                        if (!dryRun) {
                            InstanceConfig instanceConfig = dcAdmin.getInstanceConfig(clusterName, instanceInHelixOnly);
                            String instanceName = instanceConfig.getInstanceName();
                            // on updating InstanceConfig until this tool has completed. TODO, remove this logic once migration to PropertyStore is done.
                            if (!instancesWithDisabledPartition.contains(instanceName)) {
                                ZNRecord znRecord = new ZNRecord(instanceName);
                                String path = PARTITION_DISABLED_ZNODE_PATH + instanceName;
                                if (!helixPropertyStore.create(path, znRecord, AccessOption.PERSISTENT)) {
                                    logger.error("Failed to create a ZNode for {} in datacenter {} before disabling partition.", instanceName, dcName);
                                    continue;
                                }
                            }
                            instanceConfig.setInstanceEnabledForPartition(resourceName, partitionName, false);
                            dcAdmin.setInstanceConfig(clusterName, instanceInHelixOnly, instanceConfig);
                            instancesWithDisabledPartition.add(instanceName);
                        }
                        partitionsDisabled.getAndIncrement();
                    }
                    // Disabling partition won't remove certain replica from IdealState. So replicas of this partition in Helix
                    // will be more than that in static clustermap.
                    expectMoreInHelixDuringValidate = true;
                }
            }
        }
        // update state model def if necessary
        if (!resourceIs.getStateModelDefRef().equals(stateModelDef)) {
            info("[{}] Resource {} has different state model {}. Updating it with {}", dcName.toUpperCase(), resourceName, resourceIs.getStateModelDefRef(), stateModelDef);
            resourceIs.setStateModelDefRef(stateModelDef);
            resourceModified = true;
        }
        resourceIs.setNumPartitions(resourceIs.getPartitionSet().size());
        if (resourceModified) {
            if (resourceIs.getPartitionSet().isEmpty()) {
                info("[{}] Resource {} has no partition, {}", dcName.toUpperCase(), resourceName, dryRun ? "no action as dry run" : "dropping");
                if (!dryRun) {
                    dcAdmin.dropResource(clusterName, resourceName);
                }
                resourcesDropped.getAndIncrement();
            } else {
                if (!dryRun) {
                    dcAdmin.setResourceIdealState(clusterName, resourceName, resourceIs);
                    System.out.println("------------------add resource!");
                    System.out.println(resourceName);
                    System.out.println(resourceName);
                }
                resourcesUpdated.getAndIncrement();
            }
        }
    }
    // note that disabling partition also updates InstanceConfig of certain nodes which host the partitions.
    instancesUpdated.getAndAdd(instancesWithDisabledPartition.size());
    // replica decommission thread on each datanode.
    if (helixPropertyStore != null) {
        maybeAwaitForLatch();
        for (String instanceName : instancesWithDisabledPartition) {
            String path = PARTITION_DISABLED_ZNODE_PATH + instanceName;
            if (!helixPropertyStore.remove(path, AccessOption.PERSISTENT)) {
                logger.error("Failed to remove a ZNode for {} in datacenter {} after disabling partition completed.", instanceName, dcName);
            }
        }
        helixPropertyStore.stop();
    }
    // Add what is not already in Helix under new resources.
    int fromIndex = 0;
    List<Map.Entry<String, Set<String>>> newPartitions = new ArrayList<>(partitionsToInstancesInDc.entrySet());
    while (fromIndex < newPartitions.size()) {
        String resourceName = Integer.toString(++maxResource);
        int toIndex = Math.min(fromIndex + maxPartitionsInOneResource, newPartitions.size());
        List<Map.Entry<String, Set<String>>> partitionsUnderNextResource = newPartitions.subList(fromIndex, toIndex);
        fromIndex = toIndex;
        IdealState idealState = new IdealState(resourceName);
        idealState.setStateModelDefRef(stateModelDef);
        info("[{}] Adding partitions for next resource {} in {}. {}.", dcName.toUpperCase(), resourceName, dcName, dryRun ? "Actual IdealState is not changed as dry run" : "IdealState is being updated");
        for (Map.Entry<String, Set<String>> entry : partitionsUnderNextResource) {
            String partitionName = entry.getKey();
            ArrayList<String> instances = new ArrayList<>(entry.getValue());
            Collections.shuffle(instances);
            idealState.setPreferenceList(partitionName, instances);
        }
        idealState.setNumPartitions(partitionsUnderNextResource.size());
        idealState.setReplicas(ResourceConfig.ResourceConfigConstants.ANY_LIVEINSTANCE.name());
        if (!idealState.isValid()) {
            throw new IllegalStateException("IdealState could not be validated for new resource " + resourceName);
        }
        if (!dryRun) {
            dcAdmin.addResource(clusterName, resourceName, idealState);
            info("[{}] Added {} new partitions under resource {} in datacenter {}", dcName.toUpperCase(), partitionsUnderNextResource.size(), resourceName, dcName);
        } else {
            info("[{}] Under DryRun mode, {} new partitions are added to resource {} in datacenter {}", dcName.toUpperCase(), partitionsUnderNextResource.size(), resourceName, dcName);
        }
        resourcesAdded.getAndIncrement();
    }
}
Also used : Arrays(java.util.Arrays) ClusterMapUtils(com.github.ambry.clustermap.ClusterMapUtils) SortedSet(java.util.SortedSet) LoggerFactory(org.slf4j.LoggerFactory) JSONException(org.json.JSONException) JSONObject(org.json.JSONObject) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Map(java.util.Map) SharedZkClientFactory(org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory) AccessOption(org.apache.helix.AccessOption) EnumSet(java.util.EnumSet) LeaderStandbySMD(org.apache.helix.model.LeaderStandbySMD) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ZKUtil(org.apache.helix.manager.zk.ZKUtil) Set(java.util.Set) Utils(com.github.ambry.utils.Utils) HelixPropertyStoreConfig(com.github.ambry.config.HelixPropertyStoreConfig) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord) CountDownLatch(java.util.concurrent.CountDownLatch) List(java.util.List) Optional(java.util.Optional) IdealState(org.apache.helix.model.IdealState) CommonUtils(com.github.ambry.commons.CommonUtils) HashMap(java.util.HashMap) HelixZkClient(org.apache.helix.zookeeper.api.client.HelixZkClient) ResourceConfig(org.apache.helix.model.ResourceConfig) TreeSet(java.util.TreeSet) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) HelixPropertyStore(org.apache.helix.store.HelixPropertyStore) RealmAwareZkClient(org.apache.helix.zookeeper.api.client.RealmAwareZkClient) DataNodeConfigSourceType(com.github.ambry.clustermap.DataNodeConfigSourceType) StateModelDefinition(org.apache.helix.model.StateModelDefinition) Properties(java.util.Properties) Logger(org.slf4j.Logger) VerifiableProperties(com.github.ambry.config.VerifiableProperties) IOException(java.io.IOException) InstanceConfig(org.apache.helix.model.InstanceConfig) File(java.io.File) TimeUnit(java.util.concurrent.TimeUnit) HelixAdmin(org.apache.helix.HelixAdmin) TreeMap(java.util.TreeMap) ZNRecordSerializer(org.apache.helix.manager.zk.ZNRecordSerializer) ClusterMapConfig(com.github.ambry.config.ClusterMapConfig) ZKHelixAdmin(org.apache.helix.manager.zk.ZKHelixAdmin) Comparator(java.util.Comparator) Collections(java.util.Collections) SortedSet(java.util.SortedSet) EnumSet(java.util.EnumSet) Set(java.util.Set) TreeSet(java.util.TreeSet) HashSet(java.util.HashSet) ArrayList(java.util.ArrayList) HelixAdmin(org.apache.helix.HelixAdmin) ZKHelixAdmin(org.apache.helix.manager.zk.ZKHelixAdmin) IdealState(org.apache.helix.model.IdealState) InstanceConfig(org.apache.helix.model.InstanceConfig) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap) ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord) HashSet(java.util.HashSet)

Aggregations

ClusterMapUtils (com.github.ambry.clustermap.ClusterMapUtils)1 DataNodeConfigSourceType (com.github.ambry.clustermap.DataNodeConfigSourceType)1 CommonUtils (com.github.ambry.commons.CommonUtils)1 ClusterMapConfig (com.github.ambry.config.ClusterMapConfig)1 HelixPropertyStoreConfig (com.github.ambry.config.HelixPropertyStoreConfig)1 VerifiableProperties (com.github.ambry.config.VerifiableProperties)1 Utils (com.github.ambry.utils.Utils)1 File (java.io.File)1 IOException (java.io.IOException)1 ArrayList (java.util.ArrayList)1 Arrays (java.util.Arrays)1 Collections (java.util.Collections)1 Comparator (java.util.Comparator)1 EnumSet (java.util.EnumSet)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 List (java.util.List)1 Map (java.util.Map)1 Objects (java.util.Objects)1 Optional (java.util.Optional)1