Search in sources :

Example 66 with InstanceConfig

use of org.apache.helix.model.InstanceConfig in project ambry by linkedin.

the class MockHelixDataAccessor method getProperty.

@Override
public <T extends HelixProperty> List<T> getProperty(List<PropertyKey> keys, boolean throwException) {
    List<T> result = new ArrayList<>();
    for (PropertyKey key : keys) {
        if (key.toString().matches("/Ambry-/INSTANCES/.*/CURRENTSTATES/\\d+/\\d+")) {
            // an example for the key: /Ambry-/INSTANCES/localhost_18089/CURRENTSTATES/sessionId/0
            String[] segments = key.toString().split("/");
            String instanceName = segments[3];
            String resourceName = segments[6];
            Map<String, Map<String, String>> partitionStateMap = mockHelixAdmin.getPartitionStateMapForInstance(instanceName);
            ZNRecord record = new ZNRecord(resourceName);
            record.setMapFields(partitionStateMap);
            result.add((T) (new CurrentState(record)));
        } else if (key.toString().matches("/Ambry-/LIVEINSTANCES/.*_\\d+")) {
            String[] segments = key.toString().split("/");
            String instanceName = segments[3];
            ZNRecord record = new ZNRecord(instanceName);
            record.setEphemeralOwner(SESSION_ID);
            result.add((T) (new LiveInstance(record)));
        } else if (key.toString().matches("/Ambry-/CONFIGS/PARTICIPANT/.*_\\d+")) {
            String[] segments = key.toString().split("/");
            String instanceName = segments[4];
            InstanceConfig instanceConfig = mockHelixAdmin.getInstanceConfigs(clusterName).stream().filter(config -> config.getInstanceName().equals(instanceName)).findFirst().get();
            result.add((T) instanceConfig);
        } else {
            result.add((T) properties.get(key));
        }
    }
    return result;
}
Also used : LiveInstance(org.apache.helix.model.LiveInstance) InstanceConfig(org.apache.helix.model.InstanceConfig) CurrentState(org.apache.helix.model.CurrentState) ArrayList(java.util.ArrayList) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Map(java.util.Map) PropertyKey(org.apache.helix.PropertyKey) ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord)

Example 67 with InstanceConfig

use of org.apache.helix.model.InstanceConfig 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)

Example 68 with InstanceConfig

use of org.apache.helix.model.InstanceConfig in project ambry by linkedin.

the class HelixBootstrapUpgradeUtil method createInstanceConfigFromStaticInfo.

/**
 * Create an {@link InstanceConfig} for the given node from the static cluster information.
 * @param node the {@link DataNodeId}
 * @param partitionToInstances the map of partitions to instances that will be populated for this instance.
 * @param instanceToDiskReplicasMap the map of instances to the map of disk to set of replicas.
 * @param referenceInstanceConfig the InstanceConfig used to set the fields that are not derived from the json files.
 *                                These are the SEALED state and STOPPED_REPLICAS configurations. If this field is null,
 *                                then these fields are derived from the json files. This can happen if this is a newly
 *                                added node.
 * @return the constructed {@link InstanceConfig}
 */
static InstanceConfig createInstanceConfigFromStaticInfo(DataNodeId node, Map<String, Set<String>> partitionToInstances, ConcurrentHashMap<String, Map<DiskId, SortedSet<Replica>>> instanceToDiskReplicasMap, InstanceConfig referenceInstanceConfig) {
    String instanceName = getInstanceName(node);
    InstanceConfig instanceConfig = new InstanceConfig(instanceName);
    instanceConfig.setHostName(node.getHostname());
    instanceConfig.setPort(Integer.toString(node.getPort()));
    if (node.hasSSLPort()) {
        instanceConfig.getRecord().setSimpleField(SSL_PORT_STR, Integer.toString(node.getSSLPort()));
    }
    if (node.hasHttp2Port()) {
        instanceConfig.getRecord().setSimpleField(HTTP2_PORT_STR, Integer.toString(node.getHttp2Port()));
    }
    instanceConfig.getRecord().setSimpleField(DATACENTER_STR, node.getDatacenterName());
    instanceConfig.getRecord().setSimpleField(RACKID_STR, node.getRackId());
    long xid = node.getXid();
    if (xid != DEFAULT_XID) {
        // Set the XID only if it is not the default, in order to avoid unnecessary updates.
        instanceConfig.getRecord().setSimpleField(XID_STR, Long.toString(node.getXid()));
    }
    instanceConfig.getRecord().setSimpleField(SCHEMA_VERSION_STR, Integer.toString(CURRENT_SCHEMA_VERSION));
    List<String> sealedPartitionsList = new ArrayList<>();
    List<String> stoppedReplicasList = new ArrayList<>();
    List<String> disabledReplicasList = new ArrayList<>();
    if (instanceToDiskReplicasMap.containsKey(instanceName)) {
        Map<String, Map<String, String>> diskInfos = new TreeMap<>();
        for (HashMap.Entry<DiskId, SortedSet<Replica>> diskToReplicas : instanceToDiskReplicasMap.get(instanceName).entrySet()) {
            DiskId disk = diskToReplicas.getKey();
            SortedSet<Replica> replicasInDisk = diskToReplicas.getValue();
            // Note: An instance config has to contain the information for each disk about the replicas it hosts.
            // This information will be initialized to the empty string - but will be updated whenever the partition
            // is added to the cluster.
            StringBuilder replicasStrBuilder = new StringBuilder();
            for (ReplicaId replicaId : replicasInDisk) {
                Replica replica = (Replica) replicaId;
                replicasStrBuilder.append(replica.getPartition().getId()).append(REPLICAS_STR_SEPARATOR).append(replica.getCapacityInBytes()).append(REPLICAS_STR_SEPARATOR).append(replica.getPartition().getPartitionClass()).append(REPLICAS_DELIM_STR);
                if (referenceInstanceConfig == null && replica.isSealed()) {
                    sealedPartitionsList.add(Long.toString(replica.getPartition().getId()));
                }
                partitionToInstances.computeIfAbsent(Long.toString(replica.getPartition().getId()), k -> new HashSet<>()).add(instanceName);
            }
            Map<String, String> diskInfo = new HashMap<>();
            diskInfo.put(DISK_CAPACITY_STR, Long.toString(disk.getRawCapacityInBytes()));
            diskInfo.put(DISK_STATE, AVAILABLE_STR);
            diskInfo.put(REPLICAS_STR, replicasStrBuilder.toString());
            diskInfos.put(disk.getMountPath(), diskInfo);
        }
        instanceConfig.getRecord().setMapFields(diskInfos);
    }
    // Set the fields that need to be preserved from the referenceInstanceConfig.
    if (referenceInstanceConfig != null) {
        sealedPartitionsList = ClusterMapUtils.getSealedReplicas(referenceInstanceConfig);
        stoppedReplicasList = ClusterMapUtils.getStoppedReplicas(referenceInstanceConfig);
        disabledReplicasList = ClusterMapUtils.getDisabledReplicas(referenceInstanceConfig);
    }
    instanceConfig.getRecord().setListField(SEALED_STR, sealedPartitionsList);
    instanceConfig.getRecord().setListField(STOPPED_REPLICAS_STR, stoppedReplicasList);
    instanceConfig.getRecord().setListField(DISABLED_REPLICAS_STR, disabledReplicasList);
    return instanceConfig;
}
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) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) TreeMap(java.util.TreeMap) SortedSet(java.util.SortedSet) InstanceConfig(org.apache.helix.model.InstanceConfig) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap) HashSet(java.util.HashSet)

Example 69 with InstanceConfig

use of org.apache.helix.model.InstanceConfig in project ambry by linkedin.

the class HelixClusterManagerTest method xidTest.

/**
 * Tests that if the xid of an InstanceConfig change is greater than the current xid of the cluster manager, then that
 * change is ignored - both during initialization as well as with post-initialization InstanceConfig changes.
 */
@Test
public void xidTest() throws Exception {
    assumeTrue(!useComposite && listenCrossColo);
    // Close the one initialized in the constructor, as this test needs to test initialization flow as well.
    clusterManager.close();
    // Initialization path:
    MockHelixManagerFactory helixManagerFactory = new MockHelixManagerFactory(helixCluster, null, null);
    List<InstanceConfig> instanceConfigs = helixCluster.getInstanceConfigsFromDcs(helixDcs);
    int instanceCount = instanceConfigs.size();
    // find the self instance config and put it at the end of list. This is to ensure subsequent test won't choose self instance config.
    for (int i = 0; i < instanceCount; ++i) {
        if (instanceConfigs.get(i).getInstanceName().equals(selfInstanceName)) {
            Collections.swap(instanceConfigs, i, instanceConfigs.size() - 1);
            break;
        }
    }
    int randomIndex = com.github.ambry.utils.TestUtils.RANDOM.nextInt(instanceCount - 1);
    InstanceConfig aheadInstanceConfig = instanceConfigs.get(randomIndex);
    Collections.swap(instanceConfigs, randomIndex, instanceConfigs.size() - 2);
    aheadInstanceConfig.getRecord().setSimpleField(XID_STR, Long.toString(CURRENT_XID + 1));
    clusterManager = new HelixClusterManager(clusterMapConfig, selfInstanceName, helixManagerFactory, new MetricRegistry());
    assertEquals(instanceCount + numCloudDcs - 1, clusterManager.getDataNodeIds().size());
    for (DataNodeId dataNode : clusterManager.getDataNodeIds()) {
        String instanceName = ClusterMapUtils.getInstanceName(dataNode.getHostname(), dataNode.getPort());
        assertThat(instanceName, not(aheadInstanceConfig.getInstanceName()));
    }
    // Ahead instance should be honored if the cluster manager is of the aheadInstance.
    try (HelixClusterManager aheadInstanceClusterManager = new HelixClusterManager(clusterMapConfig, aheadInstanceConfig.getInstanceName(), helixManagerFactory, new MetricRegistry())) {
        assertEquals(instanceCount + numCloudDcs, aheadInstanceClusterManager.getDataNodeIds().size());
    }
    // Post-initialization InstanceConfig change: pick an instance that is neither previous instance nor self instance
    InstanceConfig ignoreInstanceConfig = instanceConfigs.get(com.github.ambry.utils.TestUtils.RANDOM.nextInt(instanceCount - 2));
    String ignoreInstanceName = ignoreInstanceConfig.getInstanceName();
    ignoreInstanceConfig.getRecord().setSimpleField(XID_STR, Long.toString(CURRENT_XID + 2));
    AmbryReplica ignoreInstanceReplica = null;
    for (DataNodeId dataNode : clusterManager.getDataNodeIds()) {
        String instanceName = ClusterMapUtils.getInstanceName(dataNode.getHostname(), dataNode.getPort());
        if (instanceName.equals(ignoreInstanceName)) {
            ignoreInstanceReplica = (AmbryReplica) clusterManager.getReplicaIds(dataNode).get(0);
            ignoreInstanceConfig.getRecord().setListField(STOPPED_REPLICAS_STR, Collections.singletonList(ignoreInstanceReplica.getPartitionId().toPathString()));
            break;
        }
    }
    helixCluster.triggerInstanceConfigChangeNotification();
    // Because the XID was higher, the change reflecting this replica being stopped will not be absorbed.
    assertFalse(ignoreInstanceReplica.isDown());
    // Now advance the current xid of the cluster manager (simulated by moving back the xid in the InstanceConfig).
    ignoreInstanceConfig.getRecord().setSimpleField(XID_STR, Long.toString(CURRENT_XID - 2));
    helixCluster.triggerInstanceConfigChangeNotification();
    // Now the change should get absorbed.
    assertTrue(ignoreInstanceReplica.isDown());
}
Also used : InstanceConfig(org.apache.helix.model.InstanceConfig) MetricRegistry(com.codahale.metrics.MetricRegistry) Test(org.junit.Test)

Example 70 with InstanceConfig

use of org.apache.helix.model.InstanceConfig in project ambry by linkedin.

the class HelixClusterManagerTest method listenCrossColoTest.

/**
 * Ensure that effects of the listenCrossColo config is as expected. When it is set to false, the Helix cluster manager
 * initializes fine, but listens to subsequent InstanceConfig changes in the local colo only.
 */
@Test
public void listenCrossColoTest() {
    assumeTrue(!useComposite);
    HelixClusterManager helixClusterManager = (HelixClusterManager) clusterManager;
    Counter instanceTriggerCounter = helixClusterManager.helixClusterManagerMetrics.instanceConfigChangeTriggerCount;
    Map<String, DcInfo> dcInfosMap = helixClusterManager.getDcInfosMap();
    Map<String, HelixManager> helixManagerMap = dcInfosMap.entrySet().stream().filter(e -> e.getValue() instanceof HelixDcInfo).collect(Collectors.toMap(Map.Entry::getKey, e -> ((HelixDcInfo) e.getValue()).helixManager));
    for (Map.Entry<String, HelixManager> entry : helixManagerMap.entrySet()) {
        if (entry.getKey().equals(localDc)) {
            assertTrue("Helix cluster manager should always be connected to the local Helix manager", entry.getValue().isConnected());
        } else {
            assertEquals("Helix cluster manager should be connected to the remote Helix managers if and only if listenCrossColo is" + "set to true", listenCrossColo, entry.getValue().isConnected());
        }
    }
    long instanceConfigChangeTriggerCount = instanceTriggerCounter.getCount();
    helixCluster.triggerInstanceConfigChangeNotification();
    assertEquals("Number of trigger count should be in accordance to listenCrossColo value", instanceConfigChangeTriggerCount + (listenCrossColo ? helixDcs.length : 1), instanceTriggerCounter.getCount());
    InstanceConfig remoteInstanceConfig = helixCluster.getInstanceConfigsFromDcs(helixDcs).stream().filter(e -> ClusterMapUtils.getDcName(e).equals(remoteDc)).findAny().get();
    DataNodeId remote = helixClusterManager.getDataNodeId(remoteInstanceConfig.getHostName(), Integer.parseInt(remoteInstanceConfig.getPort()));
    Set<PartitionId> writablePartitions = new HashSet<>(helixClusterManager.getWritablePartitionIds(null));
    PartitionId partitionIdToSealInRemote = helixClusterManager.getReplicaIds(remote).stream().filter(e -> writablePartitions.contains(e.getPartitionId())).findAny().get().getPartitionId();
    remoteInstanceConfig.getRecord().setListField(SEALED_STR, Collections.singletonList(partitionIdToSealInRemote.toPathString()));
    helixCluster.triggerInstanceConfigChangeNotification();
    assertEquals("If replica in remote is sealed, partition should be sealed if and only if listenCrossColo is true " + "and override is disabled", !listenCrossColo || overrideEnabled, helixClusterManager.getWritablePartitionIds(null).contains(partitionIdToSealInRemote));
}
Also used : CoreMatchers(org.hamcrest.CoreMatchers) Arrays(java.util.Arrays) IdealState(org.apache.helix.model.IdealState) ClusterMapUtils(com.github.ambry.clustermap.ClusterMapUtils) ServerErrorCode(com.github.ambry.server.ServerErrorCode) RunWith(org.junit.runner.RunWith) HashMap(java.util.HashMap) Random(java.util.Random) RoutingTableSnapshot(org.apache.helix.spectator.RoutingTableSnapshot) ByteBuffer(java.nio.ByteBuffer) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) MockitoAnnotations(org.mockito.MockitoAnnotations) TestUtils(com.github.ambry.clustermap.TestUtils) JSONException(org.json.JSONException) JSONObject(org.json.JSONObject) Map(java.util.Map) After(org.junit.After) Counter(com.codahale.metrics.Counter) Assume(org.junit.Assume) Parameterized(org.junit.runners.Parameterized) MetricRegistry(com.codahale.metrics.MetricRegistry) Properties(java.util.Properties) Pair(com.github.ambry.utils.Pair) Files(java.nio.file.Files) VerifiableProperties(com.github.ambry.config.VerifiableProperties) Set(java.util.Set) HelixManager(org.apache.helix.HelixManager) Utils(com.github.ambry.utils.Utils) IOException(java.io.IOException) Test(org.junit.Test) Collectors(java.util.stream.Collectors) InstanceConfig(org.apache.helix.model.InstanceConfig) File(java.io.File) ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord) Mockito(org.mockito.Mockito) List(java.util.List) ByteBufferInputStream(com.github.ambry.utils.ByteBufferInputStream) InstanceType(org.apache.helix.InstanceType) ClusterMapConfig(com.github.ambry.config.ClusterMapConfig) Gauge(com.codahale.metrics.Gauge) Assert(org.junit.Assert) Collections(java.util.Collections) ResponseHandler(com.github.ambry.commons.ResponseHandler) InputStream(java.io.InputStream) HelixManager(org.apache.helix.HelixManager) Counter(com.codahale.metrics.Counter) InstanceConfig(org.apache.helix.model.InstanceConfig) HashMap(java.util.HashMap) Map(java.util.Map) HashSet(java.util.HashSet) Test(org.junit.Test)

Aggregations

InstanceConfig (org.apache.helix.model.InstanceConfig)149 ArrayList (java.util.ArrayList)40 Test (org.testng.annotations.Test)35 HashMap (java.util.HashMap)32 HashSet (java.util.HashSet)28 ZNRecord (org.apache.helix.ZNRecord)26 IdealState (org.apache.helix.model.IdealState)24 ExternalView (org.apache.helix.model.ExternalView)23 Map (java.util.Map)21 HelixException (org.apache.helix.HelixException)21 HelixAdmin (org.apache.helix.HelixAdmin)20 List (java.util.List)19 ZKHelixAdmin (org.apache.helix.manager.zk.ZKHelixAdmin)19 HelixDataAccessor (org.apache.helix.HelixDataAccessor)17 ClusterMapConfig (com.github.ambry.config.ClusterMapConfig)15 Test (org.junit.Test)15 Set (java.util.Set)13 VerifiableProperties (com.github.ambry.config.VerifiableProperties)12 IOException (java.io.IOException)12 ZNRecord (org.apache.helix.zookeeper.datamodel.ZNRecord)12