Search in sources :

Example 21 with ZNRecord

use of org.apache.helix.zookeeper.datamodel.ZNRecord in project ambry by linkedin.

the class HelixClusterManagerTest method clusterMapOverrideEnabledAndDisabledTest.

/**
 * Test that ClusterManger will use seal state in PartitionOverride/InstanceConfig when {@link ClusterMapConfig#clusterMapEnablePartitionOverride}
 * is enabled/disabled. This test verifies that InstanceConfig changes won't affect any seal state of partition if clusterMapEnablePartitionOverride
 * is enabled. It also tests seal state can be dynamically changed by InstanceConfig change when PartitionOverride is
 * non-empty but disabled.
 */
@Test
public void clusterMapOverrideEnabledAndDisabledTest() throws Exception {
    assumeTrue(!useComposite && listenCrossColo);
    // Get the writable partitions in OverrideMap
    Set<String> writableInOverrideMap = new HashSet<>();
    for (Map.Entry<String, Map<String, String>> entry : partitionOverrideMap.entrySet()) {
        if (entry.getValue().get(ClusterMapUtils.PARTITION_STATE).equals(ClusterMapUtils.READ_WRITE_STR)) {
            writableInOverrideMap.add(entry.getKey());
        }
    }
    // Get the writable partitions in InstanceConfig(PartitionLayout)
    List<PartitionId> writableInLayout = testPartitionLayout.getPartitionLayout().getWritablePartitions(null);
    Set<String> writableInInstanceConfig = new HashSet<>();
    writableInLayout.forEach(k -> writableInInstanceConfig.add(k.toPathString()));
    if (overrideEnabled) {
        // Verify clustermap uses partition override for initialization
        Set<String> writableInClusterManager = getWritablePartitions().getSecond();
        assertEquals("Mismatch in writable partitions during initialization", writableInOverrideMap, writableInClusterManager);
        // Ensure clustermap ignores the InstanceConfig when override is enabled.
        assertFalse("Writable partitions in ClusterManager should not equal to those in InstanceConfigs when override is enabled", writableInClusterManager.equals(writableInInstanceConfig));
        // Verify writable partitions in clustermap remain unchanged when instanceConfig changes in Helix cluster
        AmbryPartition partition = (AmbryPartition) clusterManager.getWritablePartitionIds(null).get(0);
        List<String> instances = helixCluster.getInstancesForPartition((partition.toPathString()));
        Counter instanceTriggerCounter = ((HelixClusterManager) clusterManager).helixClusterManagerMetrics.instanceConfigChangeTriggerCount;
        long countVal = instanceTriggerCounter.getCount();
        helixCluster.setReplicaState(partition, instances.get(0), ReplicaStateType.SealedState, true, false);
        assertEquals("Mismatch in instanceTriggerCounter", countVal + 1, instanceTriggerCounter.getCount());
        writableInClusterManager = getWritablePartitions().getSecond();
        assertEquals("Mismatch in writable partitions when instanceConfig changes", writableInOverrideMap, writableInClusterManager);
        // Verify the partition state could be changed if this partition is not in partition override map.
        // Following test re-initializes clusterManager with new partitionLayout and then triggers instanceConfig change on new added partition
        testPartitionLayout.addNewPartitions(1, DEFAULT_PARTITION_CLASS, PartitionState.READ_WRITE, helixDcs[0]);
        Utils.writeJsonObjectToFile(testPartitionLayout.getPartitionLayout().toJSONObject(), partitionLayoutPath);
        helixCluster.upgradeWithNewPartitionLayout(partitionLayoutPath, HelixBootstrapUpgradeUtil.HelixAdminOperation.BootstrapCluster);
        clusterManager.close();
        Map<String, ZNRecord> znRecordMap = new HashMap<>();
        znRecordMap.put(PARTITION_OVERRIDE_ZNODE_PATH, znRecord);
        MockHelixManagerFactory helixManagerFactory = new MockHelixManagerFactory(helixCluster, znRecordMap, null);
        HelixClusterManager clusterManager = new HelixClusterManager(clusterMapConfig, selfInstanceName, helixManagerFactory, new MetricRegistry());
        // Ensure the new RW partition is added
        assertEquals("Mismatch in writable partitions when instanceConfig changes", writableInOverrideMap.size() + 1, clusterManager.getWritablePartitionIds(null).size());
        // Find out the new added partition which is not in partition override map
        for (PartitionId partitionId : clusterManager.getAllPartitionIds(null)) {
            if (partitionId.toPathString().equals(String.valueOf(testPartitionLayout.getPartitionCount() - 1))) {
                partition = (AmbryPartition) partitionId;
            }
        }
        instances = helixCluster.getInstancesForPartition((partition.toPathString()));
        // Change the replica from RW to RO, which triggers instanceConfig change
        helixCluster.setReplicaState(partition, instances.get(0), ReplicaStateType.SealedState, true, false);
        // Ensure the partition state becomes Read_Only
        assertFalse("If any one replica is SEALED, the whole partition should be SEALED", clusterManager.getWritablePartitionIds(null).contains(partition));
        assertEquals("If any one replica is SEALED, the whole partition should be SEALED", PartitionState.READ_ONLY, partition.getPartitionState());
    } else {
        // Verify clustermap uses instanceConfig for initialization when override map is non-empty but disabled.
        Set<String> writableInClusterManager = getWritablePartitions().getSecond();
        assertEquals("Mismatch in writable partitions during initialization", writableInInstanceConfig, writableInClusterManager);
        // Ensure clustermap ignores partition override map when override is disabled.
        assertFalse("Writable partitions in ClusterManager should not equal to those in OverrideMap when override is disabled", writableInClusterManager.equals(writableInOverrideMap));
        // Verify partition state in clustermap is changed when instanceConfig changes in Helix cluster.
        // This is to ensure partition override doesn't take any effect when it is disabled.
        AmbryPartition partition = (AmbryPartition) clusterManager.getWritablePartitionIds(null).get(0);
        List<String> instances = helixCluster.getInstancesForPartition((partition.toPathString()));
        helixCluster.setReplicaState(partition, instances.get(0), ReplicaStateType.SealedState, true, false);
        assertFalse("If any one replica is SEALED, the whole partition should be SEALED", clusterManager.getWritablePartitionIds(null).contains(partition));
        assertEquals("If any one replica is SEALED, the whole partition should be SEALED", PartitionState.READ_ONLY, partition.getPartitionState());
        // Ensure that after instanceConfig changes, the writable partitions in clusterManager match those in InstanceConfig
        writableInInstanceConfig.remove(partition.toPathString());
        writableInClusterManager = getWritablePartitions().getSecond();
        assertEquals("Mismatch in writable partitions during initialization", writableInInstanceConfig, writableInClusterManager);
    }
}
Also used : HashMap(java.util.HashMap) MetricRegistry(com.codahale.metrics.MetricRegistry) Counter(com.codahale.metrics.Counter) HashMap(java.util.HashMap) Map(java.util.Map) ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord) HashSet(java.util.HashSet) Test(org.junit.Test)

Example 22 with ZNRecord

use of org.apache.helix.zookeeper.datamodel.ZNRecord in project ambry by linkedin.

the class HelixClusterManagerTest method getNewReplicaTest.

/**
 * Test HelixClusterManager can get a new replica with given partitionId, hostname and port number.
 * @throws Exception
 */
@Test
public void getNewReplicaTest() throws Exception {
    assumeTrue(!useComposite);
    clusterManager.close();
    metricRegistry = new MetricRegistry();
    // 1. test the case where ZNRecord is NULL in Helix PropertyStore
    HelixClusterManager helixClusterManager = new HelixClusterManager(clusterMapConfig, selfInstanceName, new MockHelixManagerFactory(helixCluster, null, null), metricRegistry);
    PartitionId partitionOfNewReplica = helixClusterManager.getAllPartitionIds(null).get(0);
    DataNode dataNodeOfNewReplica = currentNode;
    assertNull("New replica should be null because no replica infos ZNRecord in Helix", helixClusterManager.getBootstrapReplica(partitionOfNewReplica.toPathString(), dataNodeOfNewReplica));
    helixClusterManager.close();
    // Prepare new replica info map in Helix property store. We use the first partition in PartitionLayout and current
    // node from constructor to build the replica info map of an existing partition. Also, we place a new partition on
    // last data node in HardwareLayout. The format should be as follows.
    // {
    // "0":{
    // "partitionClass" : "defaultPartitionClass"
    // "replicaCapacityInBytes" : "107374182400"
    // "localhost_18088" : "/mnt0"
    // }
    // "100":{
    // "partitionClass" : "defaultPartitionClass"
    // "replicaCapacityInBytes" : "107374182400"
    // "localhost_18099" : "/mnt5"
    // "localhost_18088" : "/mnt10"
    // "newhost_001"     : "/mnt0"
    // }
    // }
    Map<String, Map<String, String>> partitionToReplicaInfosMap = new HashMap<>();
    // new replica of existing partition
    Map<String, String> newReplicaInfos1 = new HashMap<>();
    newReplicaInfos1.put(PARTITION_CLASS_STR, DEFAULT_PARTITION_CLASS);
    newReplicaInfos1.put(REPLICAS_CAPACITY_STR, String.valueOf(TestPartitionLayout.defaultReplicaCapacityInBytes));
    newReplicaInfos1.put(dataNodeOfNewReplica.getHostname() + "_" + dataNodeOfNewReplica.getPort(), dataNodeOfNewReplica.getDisks().get(0).getMountPath());
    partitionToReplicaInfosMap.put(partitionOfNewReplica.toPathString(), newReplicaInfos1);
    // new replica of a new partition
    Map<String, String> newReplicaInfos2 = new HashMap<>();
    newReplicaInfos2.put(PARTITION_CLASS_STR, DEFAULT_PARTITION_CLASS);
    newReplicaInfos2.put(REPLICAS_CAPACITY_STR, String.valueOf(TestPartitionLayout.defaultReplicaCapacityInBytes));
    // find a node that is different from currentNode
    DataNode dataNodeOfNewPartition = testHardwareLayout.getAllExistingDataNodes().stream().filter(node -> node.getPort() != currentNode.getPort()).findFirst().get();
    int diskNum = dataNodeOfNewPartition.getDisks().size();
    newReplicaInfos2.put(dataNodeOfNewPartition.getHostname() + "_" + dataNodeOfNewPartition.getPort(), dataNodeOfNewPartition.getDisks().get(diskNum - 1).getMountPath());
    // add two fake entries (fake disk and fake host)
    newReplicaInfos2.put(getInstanceName(dataNodeOfNewReplica.getHostname(), dataNodeOfNewReplica.getPort()), "/mnt10");
    newReplicaInfos2.put("newhost_100", "/mnt0");
    partitionToReplicaInfosMap.put(NEW_PARTITION_ID_STR, newReplicaInfos2);
    // fake node that doesn't exist in current clustermap
    DataNodeId fakeNode = Mockito.mock(DataNode.class);
    Mockito.when(fakeNode.getHostname()).thenReturn("new_host");
    Mockito.when(fakeNode.getPort()).thenReturn(100);
    // set ZNRecord
    ZNRecord replicaInfosZNRecord = new ZNRecord(REPLICA_ADDITION_STR);
    replicaInfosZNRecord.setMapFields(partitionToReplicaInfosMap);
    // populate znRecordMap
    Map<String, ZNRecord> znRecordMap = new HashMap<>();
    znRecordMap.put(REPLICA_ADDITION_ZNODE_PATH, replicaInfosZNRecord);
    // create a new cluster manager
    metricRegistry = new MetricRegistry();
    helixClusterManager = new HelixClusterManager(clusterMapConfig, selfInstanceName, new MockHelixManagerFactory(helixCluster, znRecordMap, null), metricRegistry);
    // 2. test that cases: 1) partition is not found  2) host is not found in helix property store that associates with new replica
    // select a partition that doesn't equal to partitionOfNewReplica
    PartitionId partitionForTest;
    Random random = new Random();
    List<PartitionId> partitionsInClusterManager = helixClusterManager.getAllPartitionIds(null);
    do {
        partitionForTest = partitionsInClusterManager.get(random.nextInt(partitionsInClusterManager.size()));
    } while (partitionForTest.toString().equals(partitionOfNewReplica.toString()));
    assertNull("New replica should be null because given partition id is not in Helix property store", helixClusterManager.getBootstrapReplica(partitionForTest.toPathString(), dataNodeOfNewReplica));
    // select partitionOfNewReplica but a random node that doesn't equal to
    DataNode dataNodeForTest;
    List<DataNode> dataNodesInHardwareLayout = testHardwareLayout.getAllExistingDataNodes();
    do {
        dataNodeForTest = dataNodesInHardwareLayout.get(random.nextInt(dataNodesInHardwareLayout.size()));
    } while (dataNodeForTest == dataNodeOfNewReplica);
    assertNull("New replica should be null because hostname is not found in replica info map", helixClusterManager.getBootstrapReplica(partitionOfNewReplica.toPathString(), dataNodeForTest));
    // 3. test that new replica is from a new partition which doesn't exist in current cluster yet. (Mock adding new partition case)
    // 3.1 test new partition on host that is not present in current clustermap
    assertNull("New replica should be null because host is not present in clustermap", helixClusterManager.getBootstrapReplica(NEW_PARTITION_ID_STR, fakeNode));
    // 3.2 test new partition on disk that doesn't exist
    assertNull("New replica should be null because disk doesn't exist", helixClusterManager.getBootstrapReplica(NEW_PARTITION_ID_STR, dataNodeOfNewReplica));
    // 3.3 test replica is created for new partition
    assertNotNull("New replica should be created successfully", helixClusterManager.getBootstrapReplica(NEW_PARTITION_ID_STR, dataNodeOfNewPartition));
    // verify that boostrap replica map is empty because recently added replica is not on current node
    assertTrue("Bootstrap replica map should be empty", helixClusterManager.getBootstrapReplicaMap().isEmpty());
    // 4. test that new replica of existing partition is successfully created based on infos from Helix property store.
    assertNotNull("New replica should be created successfully", helixClusterManager.getBootstrapReplica(partitionOfNewReplica.toPathString(), dataNodeOfNewReplica));
    assertEquals("There should be exactly one entry in bootstrap replica map", 1, helixClusterManager.getBootstrapReplicaMap().size());
    helixClusterManager.close();
}
Also used : HashMap(java.util.HashMap) MetricRegistry(com.codahale.metrics.MetricRegistry) Random(java.util.Random) HashMap(java.util.HashMap) Map(java.util.Map) ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord) Test(org.junit.Test)

Example 23 with ZNRecord

use of org.apache.helix.zookeeper.datamodel.ZNRecord in project ambry by linkedin.

the class HelixParticipant method awaitDisablingPartition.

/**
 * Wait until disabling partition process has completed. This is to avoid race condition where server and Helix may
 * modify same InstanceConfig.
 * TODO remove this method after migrating ambry to PropertyStore (in Helix).
 * @throws InterruptedException
 */
private void awaitDisablingPartition() throws InterruptedException {
    Properties properties = new Properties();
    properties.setProperty("helix.property.store.root.path", "/" + clusterName + "/" + PROPERTYSTORE_STR);
    HelixPropertyStoreConfig propertyStoreConfig = new HelixPropertyStoreConfig(new VerifiableProperties(properties));
    HelixPropertyStore<ZNRecord> helixPropertyStore = CommonUtils.createHelixPropertyStore(zkConnectStr, propertyStoreConfig, null);
    String path = PARTITION_DISABLED_ZNODE_PATH + instanceName;
    int count = 1;
    while (helixPropertyStore.exists(path, AccessOption.PERSISTENT)) {
        // Thread.sleep() pauses the current thread but does not release any locks
        Thread.sleep(clusterMapConfig.clustermapRetryDisablePartitionCompletionBackoffMs);
        logger.info("{} th attempt on checking the completion of disabling partition.", ++count);
    }
    helixPropertyStore.stop();
}
Also used : VerifiableProperties(com.github.ambry.config.VerifiableProperties) HelixPropertyStoreConfig(com.github.ambry.config.HelixPropertyStoreConfig) Properties(java.util.Properties) VerifiableProperties(com.github.ambry.config.VerifiableProperties) ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord)

Example 24 with ZNRecord

use of org.apache.helix.zookeeper.datamodel.ZNRecord in project ambry by linkedin.

the class PropertyStoreToDataNodeConfigAdapter method get.

@Override
public DataNodeConfig get(String instanceName) {
    String path = CONFIG_PATH + "/" + instanceName;
    ZNRecord record = propertyStore.get(path, new Stat(), AccessOption.PERSISTENT);
    return record != null ? converter.convert(record) : null;
}
Also used : Stat(org.apache.zookeeper.data.Stat) ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord)

Example 25 with ZNRecord

use of org.apache.helix.zookeeper.datamodel.ZNRecord in project ambry by linkedin.

the class PropertyStoreToDataNodeConfigAdapter method set.

@Override
public boolean set(DataNodeConfig config) {
    ZNRecord record = converter.convert(config);
    String path = CONFIG_PATH + "/" + record.getId();
    return propertyStore.set(path, record, AccessOption.PERSISTENT);
}
Also used : ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord)

Aggregations

ZNRecord (org.apache.helix.zookeeper.datamodel.ZNRecord)37 HashMap (java.util.HashMap)19 Test (org.junit.Test)18 Map (java.util.Map)10 ArrayList (java.util.ArrayList)8 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)8 VerifiableProperties (com.github.ambry.config.VerifiableProperties)6 HashSet (java.util.HashSet)6 InstanceConfig (org.apache.helix.model.InstanceConfig)6 MetricRegistry (com.codahale.metrics.MetricRegistry)5 Properties (java.util.Properties)5 Stat (org.apache.zookeeper.data.Stat)5 ClusterMapConfig (com.github.ambry.config.ClusterMapConfig)4 HelixPropertyStoreConfig (com.github.ambry.config.HelixPropertyStoreConfig)4 IOException (java.io.IOException)4 JSONObject (org.json.JSONObject)4 List (java.util.List)3 Random (java.util.Random)3 PropertyKey (org.apache.helix.PropertyKey)3 IdealState (org.apache.helix.model.IdealState)3