This is an automated email from the ASF dual-hosted git repository. hulee pushed a commit to branch zooscalability in repository https://gitbox.apache.org/repos/asf/helix.git
commit 0709544126b61f0678f1d934f32e585f6a68433b Author: Hunter Lee <[email protected]> AuthorDate: Wed Mar 4 00:01:40 2020 -0800 Make RealmAwareZkClient implementations use HttpRoutingDataReader for routing data (#819) We want all implementations of RealmAwareZkClient to do a one-time query to Metadata Store Directory Service for routing data and cache it in memory. In order to accomplish that, we have introduced HttpRoutingDataReader, which is a Singleton class that makes a REST call to read routing data and caches it in memory. This diff updates the initialization logic in RealmAwareZkClients accordingly. Changelist: 1. Update all RealmAwareZkClient initialization logic 2. Fix tests --- .../main/java/org/apache/helix/ConfigAccessor.java | 6 +- .../mock/MockMetadataStoreDirectoryServer.java | 6 +- .../helix/msdcommon/constant/TestConstants.java | 26 ++-- .../mock/TestMockMetadataStoreDirectoryServer.java | 19 ++- .../api/factory/RealmAwareZkClientFactory.java | 26 ++-- .../zookeeper/impl/client/DedicatedZkClient.java | 55 +++++--- .../zookeeper/impl/client/FederatedZkClient.java | 25 +++- .../zookeeper/impl/client/SharedZkClient.java | 32 +++-- .../impl/factory/DedicatedZkClientFactory.java | 10 +- .../impl/factory/SharedZkClientFactory.java | 17 +-- .../zookeeper/util/HttpRoutingDataReader.java | 3 +- zookeeper-api/src/test/conf/testng.xml | 4 +- .../helix/zookeeper/constant/TestConstants.java | 45 +++++++ .../apache/helix/zookeeper/impl/ZkTestBase.java | 5 +- ...java => RealmAwareZkClientFactoryTestBase.java} | 100 ++++++++------ .../impl/client/RealmAwareZkClientTestBase.java | 150 ++++----------------- .../impl/client/TestDedicatedZkClient.java | 8 +- .../impl/client/TestFederatedZkClient.java | 53 ++------ .../zookeeper/impl/client/TestSharedZkClient.java | 8 +- .../zookeeper/util/TestHttpRoutingDataReader.java | 35 ++--- 20 files changed, 296 insertions(+), 337 deletions(-) diff --git a/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java b/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java index 0751886..8e36f83 100644 --- a/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java +++ b/helix-core/src/main/java/org/apache/helix/ConfigAccessor.java @@ -89,10 +89,8 @@ public class ConfigAccessor { private ConfigAccessor(Builder builder) throws IOException, InvalidRoutingDataException { switch (builder._realmMode) { case MULTI_REALM: - // TODO: make sure FederatedZkClient is created correctly - // TODO: pass in MSDS endpoint or pass in _realmAwareZkConnectionConfig - String msdsEndpoint = builder._realmAwareZkConnectionConfig.getMsdsEndpoint(); - _zkClient = new FederatedZkClient(); + _zkClient = new FederatedZkClient(builder._realmAwareZkConnectionConfig, + builder._realmAwareZkClientConfig); break; case SINGLE_REALM: // Create a HelixZkClient: Use a SharedZkClient because ConfigAccessor does not need to do diff --git a/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/mock/MockMetadataStoreDirectoryServer.java b/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/mock/MockMetadataStoreDirectoryServer.java index f2a59d6..7253092 100644 --- a/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/mock/MockMetadataStoreDirectoryServer.java +++ b/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/mock/MockMetadataStoreDirectoryServer.java @@ -23,7 +23,6 @@ import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executors; @@ -68,7 +67,7 @@ public class MockMetadataStoreDirectoryServer { /** * Constructs a Mock MSDS. * A sample GET might look like the following: - * curl localhost:11000/admin/v2/namespaces/MY-HELIX-NAMESPACE/METADATA_STORE_ROUTING_DATA/zk-1 + * curl localhost:11000/admin/v2/namespaces/MY-HELIX-NAMESPACE/metadata-store-realms/zk-1 * @param hostname hostname for the REST server. E.g.) "localhost" * @param port port to use. E.g.) 11000 * @param namespace the Helix REST namespace to mock. E.g.) "MY-HELIX-NAMESPACE" @@ -94,8 +93,7 @@ public class MockMetadataStoreDirectoryServer { _routingDataMap = routingData; } - public void startServer() - throws IOException { + public void startServer() throws IOException { _server = HttpServer.create(new InetSocketAddress(_hostname, _mockServerPort), 0); generateContexts(); _server.setExecutor(_executor); diff --git a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestDedicatedZkClient.java b/metadata-store-directory-common/src/test/java/org/apache/helix/msdcommon/constant/TestConstants.java similarity index 53% copy from zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestDedicatedZkClient.java copy to metadata-store-directory-common/src/test/java/org/apache/helix/msdcommon/constant/TestConstants.java index 8cf3f85..c471a1a 100644 --- a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestDedicatedZkClient.java +++ b/metadata-store-directory-common/src/test/java/org/apache/helix/msdcommon/constant/TestConstants.java @@ -1,4 +1,4 @@ -package org.apache.helix.zookeeper.impl.client; +package org.apache.helix.msdcommon.constant; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -9,7 +9,7 @@ package org.apache.helix.zookeeper.impl.client; * "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 + * 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 @@ -19,17 +19,19 @@ package org.apache.helix.zookeeper.impl.client; * under the License. */ -import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory; -import org.testng.annotations.BeforeClass; +import java.util.Collection; +import java.util.Map; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; -public class TestDedicatedZkClient extends RealmAwareZkClientTestBase { - @BeforeClass - public void beforeClass() - throws Exception { - super.beforeClass(); - // Set the factory to DedicatedZkClientFactory - _realmAwareZkClientFactory = DedicatedZkClientFactory.getInstance(); - } +/** + * Constants to be used for testing. + */ +public class TestConstants { + public static final Map<String, Collection<String>> FAKE_ROUTING_DATA = ImmutableMap.of( + "zk-0", ImmutableList.of("/sharding-key-0", "/sharding-key-1", "/sharding-key-2"), + "zk-1", ImmutableList.of("/sharding-key-3", "/sharding-key-4", "/sharding-key-5"), + "zk-2", ImmutableList.of("/sharding-key-6", "/sharding-key-7", "/sharding-key-8")); } diff --git a/metadata-store-directory-common/src/test/java/org/apache/helix/msdcommon/mock/TestMockMetadataStoreDirectoryServer.java b/metadata-store-directory-common/src/test/java/org/apache/helix/msdcommon/mock/TestMockMetadataStoreDirectoryServer.java index 1dc006a..015bc8c 100644 --- a/metadata-store-directory-common/src/test/java/org/apache/helix/msdcommon/mock/TestMockMetadataStoreDirectoryServer.java +++ b/metadata-store-directory-common/src/test/java/org/apache/helix/msdcommon/mock/TestMockMetadataStoreDirectoryServer.java @@ -22,12 +22,14 @@ package org.apache.helix.msdcommon.mock; import java.io.IOException; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; import org.apache.helix.msdcommon.constant.MetadataStoreRoutingConstants; +import org.apache.helix.msdcommon.constant.TestConstants; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; @@ -40,12 +42,6 @@ import org.testng.Assert; public class TestMockMetadataStoreDirectoryServer { @Test public void testMockMetadataStoreDirectoryServer() throws IOException { - // Create fake routing data - Map<String, Collection<String>> routingData = new HashMap<>(); - routingData.put("zk-0", ImmutableList.of("sharding-key-0", "sharding-key-1", "sharding-key-2")); - routingData.put("zk-1", ImmutableList.of("sharding-key-3", "sharding-key-4", "sharding-key-5")); - routingData.put("zk-2", ImmutableList.of("sharding-key-6", "sharding-key-7", "sharding-key-8")); - // Start MockMSDS String host = "localhost"; int port = 11000; @@ -53,7 +49,8 @@ public class TestMockMetadataStoreDirectoryServer { String namespace = "MY-HELIX-NAMESPACE"; MockMetadataStoreDirectoryServer server = - new MockMetadataStoreDirectoryServer(host, port, namespace, routingData); + new MockMetadataStoreDirectoryServer(host, port, namespace, + TestConstants.FAKE_ROUTING_DATA); server.startServer(); try (CloseableHttpClient httpClient = HttpClients.createDefault()) { // Send a GET request for all routing data @@ -69,13 +66,13 @@ public class TestMockMetadataStoreDirectoryServer { Collection<String> allRealms = routingDataList.stream().map(mapEntry -> (String) mapEntry .get(MetadataStoreRoutingConstants.SINGLE_METADATA_STORE_REALM)) .collect(Collectors.toSet()); - Assert.assertEquals(allRealms, routingData.keySet()); + Assert.assertEquals(new HashSet(allRealms), TestConstants.FAKE_ROUTING_DATA.keySet()); Map<String, List<String>> retrievedRoutingData = routingDataList.stream().collect(Collectors .toMap(mapEntry -> (String) mapEntry .get(MetadataStoreRoutingConstants.SINGLE_METADATA_STORE_REALM), mapEntry -> (List<String>) mapEntry .get(MetadataStoreRoutingConstants.SHARDING_KEYS))); - Assert.assertEquals(retrievedRoutingData, routingData); + Assert.assertEquals(retrievedRoutingData, TestConstants.FAKE_ROUTING_DATA); // Send a GET request for all realms getRequest = new HttpGet(endpoint + MockMetadataStoreDirectoryServer.REST_PREFIX + namespace @@ -86,7 +83,7 @@ public class TestMockMetadataStoreDirectoryServer { Assert.assertTrue( allRealmsMap.containsKey(MetadataStoreRoutingConstants.METADATA_STORE_REALMS)); allRealms = allRealmsMap.get(MetadataStoreRoutingConstants.METADATA_STORE_REALMS); - Assert.assertEquals(allRealms, routingData.keySet()); + Assert.assertEquals(allRealms, TestConstants.FAKE_ROUTING_DATA.keySet()); // Send a GET request for testZkRealm String testZkRealm = "zk-0"; @@ -103,7 +100,7 @@ public class TestMockMetadataStoreDirectoryServer { Collection<String> shardingKeyList = (Collection) shardingKeysMap.get(MetadataStoreRoutingConstants.SHARDING_KEYS); Assert.assertEquals(zkRealm, testZkRealm); - Assert.assertEquals(shardingKeyList, routingData.get(testZkRealm)); + Assert.assertEquals(shardingKeyList, TestConstants.FAKE_ROUTING_DATA.get(testZkRealm)); // Try sending a POST request (not supported) HttpPost postRequest = new HttpPost( diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/factory/RealmAwareZkClientFactory.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/factory/RealmAwareZkClientFactory.java index cdfa778..d8fa9a7 100644 --- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/factory/RealmAwareZkClientFactory.java +++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/api/factory/RealmAwareZkClientFactory.java @@ -19,7 +19,9 @@ package org.apache.helix.zookeeper.api.factory; * under the License. */ -import org.apache.helix.msdcommon.datamodel.MetadataStoreRoutingData; +import java.io.IOException; + +import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; import org.apache.helix.zookeeper.api.client.RealmAwareZkClient; @@ -31,26 +33,24 @@ public interface RealmAwareZkClientFactory { * Build a RealmAwareZkClient using specified connection config and client config. * @param connectionConfig * @param clientConfig - * @param metadataStoreRoutingData - * @return HelixZkClient + * @return RealmAwareZkClient + * @throws IOException if Metadata Store Directory Service is unresponsive over HTTP + * @throws InvalidRoutingDataException if the routing data received is invalid or empty */ - // TODO: remove MetadataStoreRoutingData RealmAwareZkClient buildZkClient(RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig, - RealmAwareZkClient.RealmAwareZkClientConfig clientConfig, - MetadataStoreRoutingData metadataStoreRoutingData); + RealmAwareZkClient.RealmAwareZkClientConfig clientConfig) + throws IOException, InvalidRoutingDataException; /** * Builds a RealmAwareZkClient using specified connection config and default client config. * @param connectionConfig - * @param metadataStoreRoutingData * @return RealmAwareZkClient + * @throws IOException if Metadata Store Directory Service is unresponsive over HTTP + * @throws InvalidRoutingDataException if the routing data received is invalid or empty */ - - // TODO: remove MetadataStoreRoutingData default RealmAwareZkClient buildZkClient( - RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig, - MetadataStoreRoutingData metadataStoreRoutingData) { - return buildZkClient(connectionConfig, new RealmAwareZkClient.RealmAwareZkClientConfig(), - metadataStoreRoutingData); + RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig) + throws IOException, InvalidRoutingDataException { + return buildZkClient(connectionConfig, new RealmAwareZkClient.RealmAwareZkClientConfig()); } } diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/DedicatedZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/DedicatedZkClient.java index 8352b2b..beeb085 100644 --- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/DedicatedZkClient.java +++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/DedicatedZkClient.java @@ -19,12 +19,15 @@ package org.apache.helix.zookeeper.impl.client; * under the License. */ +import java.io.IOException; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.TimeUnit; import org.apache.helix.msdcommon.datamodel.MetadataStoreRoutingData; +import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; import org.apache.helix.zookeeper.api.client.RealmAwareZkClient; +import org.apache.helix.zookeeper.util.HttpRoutingDataReader; import org.apache.helix.zookeeper.zkclient.DataUpdater; import org.apache.helix.zookeeper.zkclient.IZkChildListener; import org.apache.helix.zookeeper.zkclient.IZkConnection; @@ -56,24 +59,39 @@ public class DedicatedZkClient implements RealmAwareZkClient { private final ZkClient _rawZkClient; private final MetadataStoreRoutingData _metadataStoreRoutingData; private final String _zkRealmShardingKey; - private final String _zkRealmAddress; - // TODO: Remove MetadataStoreRoutingData from constructor + /** + * DedicatedZkClient connects to a single ZK realm and supports full ZkClient functionalities + * such as CRUD, change callback, and ephemeral operations for a single ZkRealmShardingKey. + * @param connectionConfig + * @param clientConfig + * @throws IOException + * @throws InvalidRoutingDataException + */ public DedicatedZkClient(RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig, - RealmAwareZkClient.RealmAwareZkClientConfig clientConfig, - MetadataStoreRoutingData metadataStoreRoutingData) { - + RealmAwareZkClient.RealmAwareZkClientConfig clientConfig) + throws IOException, InvalidRoutingDataException { if (connectionConfig == null) { throw new IllegalArgumentException("RealmAwareZkConnectionConfig cannot be null!"); } - _zkRealmShardingKey = connectionConfig.getZkRealmShardingKey(); + if (clientConfig == null) { + throw new IllegalArgumentException("RealmAwareZkClientConfig cannot be null!"); + } - if (metadataStoreRoutingData == null) { - throw new IllegalArgumentException("MetadataStoreRoutingData cannot be null!"); + // Get the routing data from a static Singleton HttpRoutingDataReader + String msdsEndpoint = connectionConfig.getMsdsEndpoint(); + if (msdsEndpoint == null || msdsEndpoint.isEmpty()) { + _metadataStoreRoutingData = HttpRoutingDataReader.getMetadataStoreRoutingData(); + } else { + _metadataStoreRoutingData = HttpRoutingDataReader.getMetadataStoreRoutingData(msdsEndpoint); + } + + _zkRealmShardingKey = connectionConfig.getZkRealmShardingKey(); + if (_zkRealmShardingKey == null || _zkRealmShardingKey.isEmpty()) { + throw new IllegalArgumentException( + "RealmAwareZkConnectionConfig's ZK realm sharding key cannot be null or empty for DedicatedZkClient!"); } - _metadataStoreRoutingData = metadataStoreRoutingData; - // TODO: Get it from static map/singleton (HttpRoutingDataReader) // Get the ZkRealm address based on the ZK path sharding key String zkRealmAddress = _metadataStoreRoutingData.getMetadataStoreRealm(_zkRealmShardingKey); if (zkRealmAddress == null || zkRealmAddress.isEmpty()) { @@ -81,7 +99,6 @@ public class DedicatedZkClient implements RealmAwareZkClient { "ZK realm address for the given ZK realm sharding key is invalid! ZK realm address: " + zkRealmAddress + " ZK realm sharding key: " + _zkRealmShardingKey); } - _zkRealmAddress = zkRealmAddress; // Create a ZK connection IZkConnection zkConnection = @@ -457,17 +474,17 @@ public class DedicatedZkClient implements RealmAwareZkClient { * @return */ private void checkIfPathContainsShardingKey(String path) { - // TODO: replace with the singleton MetadataStoreRoutingData try { - String zkRealmForPath = _metadataStoreRoutingData.getMetadataStoreRealm(path); - if (!_zkRealmAddress.equals(zkRealmForPath)) { - throw new IllegalArgumentException("Given path: " + path + "'s ZK realm: " + zkRealmForPath - + " does not match the ZK realm: " + _zkRealmAddress + " and sharding key: " - + _zkRealmShardingKey + " for this DedicatedZkClient!"); + String targetShardingKey = _metadataStoreRoutingData.getShardingKeyInPath(path); + if (!_zkRealmShardingKey.equals(targetShardingKey)) { + throw new IllegalArgumentException( + "Given path: " + path + "'s ZK sharding key: " + targetShardingKey + + " does not match the ZK sharding key: " + _zkRealmShardingKey + + " for this DedicatedZkClient!"); } } catch (NoSuchElementException e) { - throw new IllegalArgumentException( - "Given path: " + path + " does not have a valid sharding key!"); + throw new IllegalArgumentException("Given path: " + path + + " does not have a valid sharding key or its ZK sharding key is not found in the cached routing data!"); } } } diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java index 5f63408..1bfff66 100644 --- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java +++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/FederatedZkClient.java @@ -19,6 +19,7 @@ package org.apache.helix.zookeeper.impl.client; * under the License. */ +import java.io.IOException; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -27,14 +28,16 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import org.apache.helix.msdcommon.datamodel.MetadataStoreRoutingData; +import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; import org.apache.helix.zookeeper.api.client.RealmAwareZkClient; import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory; +import org.apache.helix.zookeeper.util.HttpRoutingDataReader; import org.apache.helix.zookeeper.zkclient.DataUpdater; import org.apache.helix.zookeeper.zkclient.IZkChildListener; import org.apache.helix.zookeeper.zkclient.IZkDataListener; +import org.apache.helix.zookeeper.zkclient.IZkStateListener; import org.apache.helix.zookeeper.zkclient.ZkConnection; import org.apache.helix.zookeeper.zkclient.callback.ZkAsyncCallbacks; -import org.apache.helix.zookeeper.zkclient.IZkStateListener; import org.apache.helix.zookeeper.zkclient.serialize.BasicZkSerializer; import org.apache.helix.zookeeper.zkclient.serialize.PathBasedZkSerializer; import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer; @@ -80,19 +83,27 @@ public class FederatedZkClient implements RealmAwareZkClient { private PathBasedZkSerializer _pathBasedZkSerializer; // TODO: support capacity of ZkClient number in one FederatedZkClient and do garbage collection. - public FederatedZkClient(RealmAwareZkClient.RealmAwareZkClientConfig clientConfig, - MetadataStoreRoutingData metadataStoreRoutingData) { - if (metadataStoreRoutingData == null) { - throw new IllegalArgumentException("MetadataStoreRoutingData cannot be null!"); + public FederatedZkClient(RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig, + RealmAwareZkClient.RealmAwareZkClientConfig clientConfig) + throws IOException, InvalidRoutingDataException { + if (connectionConfig == null) { + throw new IllegalArgumentException("RealmAwareZkConnectionConfig cannot be null!"); } if (clientConfig == null) { - throw new IllegalArgumentException("Client config cannot be null!"); + throw new IllegalArgumentException("RealmAwareZkClientConfig cannot be null!"); + } + + // Attempt to get MetadataStoreRoutingData + String msdsEndpoint = connectionConfig.getMsdsEndpoint(); + if (msdsEndpoint == null || msdsEndpoint.isEmpty()) { + _metadataStoreRoutingData = HttpRoutingDataReader.getMetadataStoreRoutingData(); + } else { + _metadataStoreRoutingData = HttpRoutingDataReader.getMetadataStoreRoutingData(msdsEndpoint); } _isClosed = false; _clientConfig = clientConfig; _pathBasedZkSerializer = clientConfig.getZkSerializer(); - _metadataStoreRoutingData = metadataStoreRoutingData; _zkRealmToZkClientMap = new ConcurrentHashMap<>(); } diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java index f2d9416..dd78aba 100644 --- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java +++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/client/SharedZkClient.java @@ -19,26 +19,27 @@ package org.apache.helix.zookeeper.impl.client; * under the License. */ +import java.io.IOException; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.TimeUnit; import org.apache.helix.msdcommon.datamodel.MetadataStoreRoutingData; +import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; import org.apache.helix.zookeeper.api.client.HelixZkClient; import org.apache.helix.zookeeper.api.client.RealmAwareZkClient; import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory; +import org.apache.helix.zookeeper.util.HttpRoutingDataReader; import org.apache.helix.zookeeper.zkclient.DataUpdater; import org.apache.helix.zookeeper.zkclient.IZkChildListener; import org.apache.helix.zookeeper.zkclient.IZkDataListener; import org.apache.helix.zookeeper.zkclient.callback.ZkAsyncCallbacks; import org.apache.helix.zookeeper.zkclient.deprecated.IZkStateListener; -import org.apache.helix.zookeeper.zkclient.exception.ZkNoNodeException; import org.apache.helix.zookeeper.zkclient.serialize.PathBasedZkSerializer; import org.apache.helix.zookeeper.zkclient.serialize.ZkSerializer; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Op; import org.apache.zookeeper.OpResult; -import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; @@ -55,25 +56,34 @@ public class SharedZkClient implements RealmAwareZkClient { private static Logger LOG = LoggerFactory.getLogger(SharedZkClient.class); private final HelixZkClient _innerSharedZkClient; - private final String _zkRealmShardingKey; private final MetadataStoreRoutingData _metadataStoreRoutingData; + private final String _zkRealmShardingKey; private final String _zkRealmAddress; public SharedZkClient(RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig, - RealmAwareZkClient.RealmAwareZkClientConfig clientConfig, - MetadataStoreRoutingData metadataStoreRoutingData) { - + RealmAwareZkClient.RealmAwareZkClientConfig clientConfig) + throws IOException, InvalidRoutingDataException { if (connectionConfig == null) { throw new IllegalArgumentException("RealmAwareZkConnectionConfig cannot be null!"); } - _zkRealmShardingKey = connectionConfig.getZkRealmShardingKey(); + if (clientConfig == null) { + throw new IllegalArgumentException("RealmAwareZkClientConfig cannot be null!"); + } - if (metadataStoreRoutingData == null) { - throw new IllegalArgumentException("MetadataStoreRoutingData cannot be null!"); + // Get the routing data from a static Singleton HttpRoutingDataReader + String msdsEndpoint = connectionConfig.getMsdsEndpoint(); + if (msdsEndpoint == null || msdsEndpoint.isEmpty()) { + _metadataStoreRoutingData = HttpRoutingDataReader.getMetadataStoreRoutingData(); + } else { + _metadataStoreRoutingData = HttpRoutingDataReader.getMetadataStoreRoutingData(msdsEndpoint); + } + + _zkRealmShardingKey = connectionConfig.getZkRealmShardingKey(); + if (_zkRealmShardingKey == null || _zkRealmShardingKey.isEmpty()) { + throw new IllegalArgumentException( + "RealmAwareZkConnectionConfig's ZK realm sharding key cannot be null or empty for SharedZkClient!"); } - _metadataStoreRoutingData = metadataStoreRoutingData; - // TODO: use _zkRealmShardingKey to generate zkRealmAddress. This can done the same way of pull 765 once @hunter check it in. // Get the ZkRealm address based on the ZK path sharding key String zkRealmAddress = _metadataStoreRoutingData.getMetadataStoreRealm(_zkRealmShardingKey); if (zkRealmAddress == null || zkRealmAddress.isEmpty()) { diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/DedicatedZkClientFactory.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/DedicatedZkClientFactory.java index 6694497..92d628c 100644 --- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/DedicatedZkClientFactory.java +++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/DedicatedZkClientFactory.java @@ -19,7 +19,9 @@ package org.apache.helix.zookeeper.impl.factory; * under the License. */ -import org.apache.helix.msdcommon.datamodel.MetadataStoreRoutingData; +import java.io.IOException; + +import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; import org.apache.helix.zookeeper.api.client.HelixZkClient; import org.apache.helix.zookeeper.api.client.RealmAwareZkClient; import org.apache.helix.zookeeper.impl.client.DedicatedZkClient; @@ -37,9 +39,9 @@ public class DedicatedZkClientFactory extends HelixZkClientFactory { @Override public RealmAwareZkClient buildZkClient( RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig, - RealmAwareZkClient.RealmAwareZkClientConfig clientConfig, - MetadataStoreRoutingData metadataStoreRoutingData) { - return new DedicatedZkClient(connectionConfig, clientConfig, metadataStoreRoutingData); + RealmAwareZkClient.RealmAwareZkClientConfig clientConfig) + throws IOException, InvalidRoutingDataException { + return new DedicatedZkClient(connectionConfig, clientConfig); } private static class SingletonHelper { diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/SharedZkClientFactory.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/SharedZkClientFactory.java index 80c58bf..fb46ef2 100644 --- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/SharedZkClientFactory.java +++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/impl/factory/SharedZkClientFactory.java @@ -19,10 +19,11 @@ package org.apache.helix.zookeeper.impl.factory; * under the License. */ +import java.io.IOException; import java.util.HashMap; import java.util.List; -import org.apache.helix.msdcommon.datamodel.MetadataStoreRoutingData; +import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; import org.apache.helix.zookeeper.api.client.HelixZkClient; import org.apache.helix.zookeeper.api.client.RealmAwareZkClient; import org.apache.helix.zookeeper.exception.ZkClientException; @@ -57,18 +58,10 @@ public class SharedZkClientFactory extends HelixZkClientFactory { @Override public RealmAwareZkClient buildZkClient( RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig, - RealmAwareZkClient.RealmAwareZkClientConfig clientConfig, - MetadataStoreRoutingData metadataStoreRoutingData) { + RealmAwareZkClient.RealmAwareZkClientConfig clientConfig) + throws IOException, InvalidRoutingDataException { // Note, the logic sharing connectionManager logic is inside SharedZkClient, similar to innerSharedZkClient. - return new SharedZkClient(connectionConfig, clientConfig, metadataStoreRoutingData); - } - - @Override - public RealmAwareZkClient buildZkClient( - RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig, - MetadataStoreRoutingData metadataStoreRoutingData) { - // TODO: Implement the logic - return null; + return new SharedZkClient(connectionConfig, clientConfig); } private static class SingletonHelper { diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/util/HttpRoutingDataReader.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/util/HttpRoutingDataReader.java index 04f6c44..b4c1f9c 100644 --- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/util/HttpRoutingDataReader.java +++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/util/HttpRoutingDataReader.java @@ -141,7 +141,8 @@ public class HttpRoutingDataReader { * @throws IOException */ private static String getAllRoutingData() throws IOException { - // Note that MSDS_ENDPOINT should provide high-availability - it risks becoming a single point of failure if it's backed by a single IP address/host + // Note that MSDS_ENDPOINT should provide high-availability - it risks becoming a single point + // of failure if it's backed by a single IP address/host // Retry count is 3 by default. HttpGet requestAllData = new HttpGet( SYSTEM_MSDS_ENDPOINT + MetadataStoreRoutingConstants.MSDS_GET_ALL_ROUTING_DATA_ENDPOINT); diff --git a/zookeeper-api/src/test/conf/testng.xml b/zookeeper-api/src/test/conf/testng.xml index 0847f47..bf78089 100644 --- a/zookeeper-api/src/test/conf/testng.xml +++ b/zookeeper-api/src/test/conf/testng.xml @@ -18,8 +18,8 @@ specific language governing permissions and limitations under the License. --> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> -<suite name="Suite" parallel="false"> - <test name="Test" preserve-order="true"> +<suite name="Suite" parallel="false" preserve-order="true"> + <test name="Test"> <packages> <package name="org.apache.helix.zookeeper.*"/> </packages> diff --git a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/constant/TestConstants.java b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/constant/TestConstants.java new file mode 100644 index 0000000..a217245 --- /dev/null +++ b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/constant/TestConstants.java @@ -0,0 +1,45 @@ +package org.apache.helix.zookeeper.constant; + +/* + * 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 java.util.Collection; +import java.util.Map; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + + +/** + * Constants to be used for testing. + */ +public class TestConstants { + // ZK hostname prefix and port to be used throughout the zookeeper-api module + public static final String ZK_PREFIX = "localhost:"; + public static final int ZK_START_PORT = 2127; + + // Based on the ZK hostname constants, construct a set of fake routing data mappings + public static final Map<String, Collection<String>> FAKE_ROUTING_DATA = ImmutableMap + .of(ZK_PREFIX + ZK_START_PORT, + ImmutableList.of("/sharding-key-0", "/sharding-key-1", "/sharding-key-2"), + ZK_PREFIX + (ZK_START_PORT + 1), + ImmutableList.of("/sharding-key-3", "/sharding-key-4", "/sharding-key-5"), + ZK_PREFIX + (ZK_START_PORT + 2), + ImmutableList.of("/sharding-key-6", "/sharding-key-7", "/sharding-key-8")); +} diff --git a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/ZkTestBase.java b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/ZkTestBase.java index 7e59652..51eda80 100644 --- a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/ZkTestBase.java +++ b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/ZkTestBase.java @@ -29,6 +29,7 @@ import javax.management.MBeanServerConnection; import javax.management.ObjectName; import org.apache.commons.io.FileUtils; +import org.apache.helix.zookeeper.constant.TestConstants; import org.apache.helix.zookeeper.zkclient.IDefaultNameSpace; import org.apache.helix.zookeeper.zkclient.ZkServer; import org.slf4j.Logger; @@ -50,8 +51,8 @@ public class ZkTestBase { private static final String MULTI_ZK_PROPERTY_KEY = "multiZk"; private static final String NUM_ZK_PROPERTY_KEY = "numZk"; - protected static final String ZK_PREFIX = "localhost:"; - protected static final int ZK_START_PORT = 2127; + public static final String ZK_PREFIX = TestConstants.ZK_PREFIX; + public static final int ZK_START_PORT = TestConstants.ZK_START_PORT; /* * Multiple ZK references diff --git a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientTestBase.java b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientFactoryTestBase.java similarity index 61% copy from zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientTestBase.java copy to zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientFactoryTestBase.java index 323d5f4..6c3555f 100644 --- a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientTestBase.java +++ b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientFactoryTestBase.java @@ -19,55 +19,44 @@ package org.apache.helix.zookeeper.impl.client; * under the License. */ -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.io.IOException; +import java.util.NoSuchElementException; -import org.apache.helix.msdcommon.datamodel.MetadataStoreRoutingData; -import org.apache.helix.msdcommon.datamodel.TrieRoutingData; +import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; import org.apache.helix.zookeeper.api.client.RealmAwareZkClient; import org.apache.helix.zookeeper.api.factory.RealmAwareZkClientFactory; import org.apache.helix.zookeeper.datamodel.ZNRecord; import org.apache.helix.zookeeper.datamodel.serializer.ZNRecordSerializer; -import org.apache.helix.zookeeper.impl.ZkTestBase; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -public abstract class RealmAwareZkClientTestBase extends ZkTestBase { - protected static final String ZK_SHARDING_KEY_PREFIX = "/TEST_SHARDING_KEY"; - protected static final String TEST_VALID_PATH = ZK_SHARDING_KEY_PREFIX + "_" + 0 + "/a/b/c"; - protected static final String TEST_INVALID_PATH = ZK_SHARDING_KEY_PREFIX + "_invalid" + "/a/b/c"; - - // <Realm, List of sharding keys> Mapping - private static final Map<String, List<String>> RAW_ROUTING_DATA = new HashMap<>(); - +/** + * Test Base for DedicatedZkClient and SharedZkClient, which are implementations of + * RealmAwareZkClient. + * This class allows TestDedicatedZkClient and TestSharedZkClient to share the common test logic by + * just swapping out the factory classes. + */ +public abstract class RealmAwareZkClientFactoryTestBase extends RealmAwareZkClientTestBase { // The following RealmAwareZkClientFactory is to be defined in subclasses protected RealmAwareZkClientFactory _realmAwareZkClientFactory; protected RealmAwareZkClient _realmAwareZkClient; - private MetadataStoreRoutingData _metadataStoreRoutingData; + private static final ZNRecord DUMMY_RECORD = new ZNRecord("DummyRecord"); @BeforeClass - public void beforeClass() throws Exception { - // Populate RAW_ROUTING_DATA - for (int i = 0; i < _numZk; i++) { - List<String> shardingKeyList = new ArrayList<>(); - shardingKeyList.add(ZK_SHARDING_KEY_PREFIX + "_" + i); - String realmName = ZK_PREFIX + (ZK_START_PORT + i); - RAW_ROUTING_DATA.put(realmName, shardingKeyList); - } - - // Feed the raw routing data into TrieRoutingData to construct an in-memory representation of routing information - _metadataStoreRoutingData = new TrieRoutingData(RAW_ROUTING_DATA); + public void beforeClass() throws IOException, InvalidRoutingDataException { + super.beforeClass(); + DUMMY_RECORD.setSimpleField("Dummy", "Value"); } @AfterClass public void afterClass() { + super.afterClass(); if (_realmAwareZkClient != null && !_realmAwareZkClient.isClosed()) { _realmAwareZkClient.close(); + _realmAwareZkClient = null; } } @@ -80,7 +69,7 @@ public abstract class RealmAwareZkClientTestBase extends ZkTestBase { @Test public void testRealmAwareZkClientCreation() { // Create a RealmAwareZkClient - String invalidShardingKey = "InvalidShardingKey"; + String invalidShardingKey = "InvalidShardingKeyNoLeadingSlash"; RealmAwareZkClient.RealmAwareZkClientConfig clientConfig = new RealmAwareZkClient.RealmAwareZkClientConfig(); @@ -91,19 +80,37 @@ public abstract class RealmAwareZkClientTestBase extends ZkTestBase { builder.setZkRealmShardingKey(invalidShardingKey).build(); try { - _realmAwareZkClient = _realmAwareZkClientFactory - .buildZkClient(connectionConfig, clientConfig, _metadataStoreRoutingData); + _realmAwareZkClient = + _realmAwareZkClientFactory.buildZkClient(connectionConfig, clientConfig); Assert.fail("Should not succeed with an invalid sharding key!"); } catch (IllegalArgumentException e) { - // Expected + // Expected because invalid sharding key would cause an IllegalArgumentException to be thrown + } catch (Exception e) { + Assert.fail("Should not see any other types of Exceptions: " + e); + } + + // Create a connection config with a valid sharding key, but one that does not exist in + // the routing data + String nonExistentShardingKey = "/NonExistentShardingKey"; + connectionConfig = builder.setZkRealmShardingKey(nonExistentShardingKey).build(); + try { + _realmAwareZkClient = + _realmAwareZkClientFactory.buildZkClient(connectionConfig, clientConfig); + Assert.fail("Should not succeed with a non-existent sharding key!"); + } catch (NoSuchElementException e) { + // Expected non-existent sharding key would cause a NoSuchElementException to be thrown + } catch (Exception e) { + Assert.fail("Should not see any other types of Exceptions: " + e); } // Use a valid sharding key this time around - String validShardingKey = ZK_SHARDING_KEY_PREFIX + "_" + 0; // Use TEST_SHARDING_KEY_0 - builder.setZkRealmShardingKey(validShardingKey); - connectionConfig = builder.build(); - _realmAwareZkClient = _realmAwareZkClientFactory - .buildZkClient(connectionConfig, clientConfig, _metadataStoreRoutingData); + connectionConfig = builder.setZkRealmShardingKey(ZK_SHARDING_KEY_PREFIX).build(); + try { + _realmAwareZkClient = + _realmAwareZkClientFactory.buildZkClient(connectionConfig, clientConfig); + } catch (Exception e) { + Assert.fail("All other exceptions not allowed: " + e); + } } /** @@ -115,14 +122,10 @@ public abstract class RealmAwareZkClientTestBase extends ZkTestBase { public void testRealmAwareZkClientCreatePersistent() { _realmAwareZkClient.setZkSerializer(new ZNRecordSerializer()); - // Create a dummy ZNRecord - ZNRecord znRecord = new ZNRecord("DummyRecord"); - znRecord.setSimpleField("Dummy", "Value"); - // Test writing and reading against the validPath _realmAwareZkClient.createPersistent(TEST_VALID_PATH, true); - _realmAwareZkClient.writeData(TEST_VALID_PATH, znRecord); - Assert.assertEquals(_realmAwareZkClient.readData(TEST_VALID_PATH), znRecord); + _realmAwareZkClient.writeData(TEST_VALID_PATH, DUMMY_RECORD); + Assert.assertEquals(_realmAwareZkClient.readData(TEST_VALID_PATH), DUMMY_RECORD); // Test writing and reading against the invalid path try { @@ -138,6 +141,12 @@ public abstract class RealmAwareZkClientTestBase extends ZkTestBase { */ @Test(dependsOnMethods = "testRealmAwareZkClientCreatePersistent") public void testExists() { + // Create a ZNode for testing + _realmAwareZkClient.createPersistent(TEST_VALID_PATH, true); + _realmAwareZkClient.writeData(TEST_VALID_PATH, DUMMY_RECORD); + Assert.assertEquals(_realmAwareZkClient.readData(TEST_VALID_PATH), DUMMY_RECORD); + + // Test exists() Assert.assertTrue(_realmAwareZkClient.exists(TEST_VALID_PATH)); try { @@ -153,9 +162,14 @@ public abstract class RealmAwareZkClientTestBase extends ZkTestBase { */ @Test(dependsOnMethods = "testExists") public void testDelete() { + // Create a ZNode for testing + _realmAwareZkClient.createPersistent(TEST_VALID_PATH, true); + _realmAwareZkClient.writeData(TEST_VALID_PATH, DUMMY_RECORD); + Assert.assertEquals(_realmAwareZkClient.readData(TEST_VALID_PATH), DUMMY_RECORD); + try { _realmAwareZkClient.delete(TEST_INVALID_PATH); - Assert.fail("Exists() should not succeed on an invalid path!"); + Assert.fail("delete() should not succeed on an invalid path!"); } catch (IllegalArgumentException e) { // Okay - expected } diff --git a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientTestBase.java b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientTestBase.java index 323d5f4..900c79f 100644 --- a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientTestBase.java +++ b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/RealmAwareZkClientTestBase.java @@ -19,148 +19,48 @@ package org.apache.helix.zookeeper.impl.client; * under the License. */ -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.io.IOException; -import org.apache.helix.msdcommon.datamodel.MetadataStoreRoutingData; -import org.apache.helix.msdcommon.datamodel.TrieRoutingData; -import org.apache.helix.zookeeper.api.client.RealmAwareZkClient; -import org.apache.helix.zookeeper.api.factory.RealmAwareZkClientFactory; -import org.apache.helix.zookeeper.datamodel.ZNRecord; -import org.apache.helix.zookeeper.datamodel.serializer.ZNRecordSerializer; +import org.apache.helix.msdcommon.constant.MetadataStoreRoutingConstants; +import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; +import org.apache.helix.msdcommon.mock.MockMetadataStoreDirectoryServer; +import org.apache.helix.zookeeper.constant.TestConstants; import org.apache.helix.zookeeper.impl.ZkTestBase; -import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; public abstract class RealmAwareZkClientTestBase extends ZkTestBase { - protected static final String ZK_SHARDING_KEY_PREFIX = "/TEST_SHARDING_KEY"; - protected static final String TEST_VALID_PATH = ZK_SHARDING_KEY_PREFIX + "_" + 0 + "/a/b/c"; + protected static final String ZK_SHARDING_KEY_PREFIX = "/sharding-key-0"; + protected static final String TEST_VALID_PATH = ZK_SHARDING_KEY_PREFIX + "/a/b/c"; protected static final String TEST_INVALID_PATH = ZK_SHARDING_KEY_PREFIX + "_invalid" + "/a/b/c"; - // <Realm, List of sharding keys> Mapping - private static final Map<String, List<String>> RAW_ROUTING_DATA = new HashMap<>(); - - // The following RealmAwareZkClientFactory is to be defined in subclasses - protected RealmAwareZkClientFactory _realmAwareZkClientFactory; - protected RealmAwareZkClient _realmAwareZkClient; - private MetadataStoreRoutingData _metadataStoreRoutingData; + // Create a MockMSDS for testing + private static MockMetadataStoreDirectoryServer _msdsServer; + private static final String MSDS_HOSTNAME = "localhost"; + private static final int MSDS_PORT = 1111; + private static final String MSDS_NAMESPACE = "test"; @BeforeClass - public void beforeClass() throws Exception { - // Populate RAW_ROUTING_DATA - for (int i = 0; i < _numZk; i++) { - List<String> shardingKeyList = new ArrayList<>(); - shardingKeyList.add(ZK_SHARDING_KEY_PREFIX + "_" + i); - String realmName = ZK_PREFIX + (ZK_START_PORT + i); - RAW_ROUTING_DATA.put(realmName, shardingKeyList); + public void beforeClass() throws IOException, InvalidRoutingDataException { + // Create a mock MSDS so that HttpRoudingDataReader could fetch the routing data + if (_msdsServer == null) { + // Do not create again if Mock MSDS server has already been created by other tests + _msdsServer = new MockMetadataStoreDirectoryServer(MSDS_HOSTNAME, MSDS_PORT, MSDS_NAMESPACE, + TestConstants.FAKE_ROUTING_DATA); + _msdsServer.startServer(); } - // Feed the raw routing data into TrieRoutingData to construct an in-memory representation of routing information - _metadataStoreRoutingData = new TrieRoutingData(RAW_ROUTING_DATA); + // Register the MSDS endpoint as a System variable + String msdsEndpoint = + "http://" + MSDS_HOSTNAME + ":" + MSDS_PORT + "/admin/v2/namespaces/" + MSDS_NAMESPACE; + System.setProperty(MetadataStoreRoutingConstants.MSDS_SERVER_ENDPOINT_KEY, msdsEndpoint); } @AfterClass public void afterClass() { - if (_realmAwareZkClient != null && !_realmAwareZkClient.isClosed()) { - _realmAwareZkClient.close(); - } - } - - /** - * 1. Create a RealmAwareZkClient with a non-existing sharding key (for which there is no valid ZK realm) - * -> This should fail with an exception - * 2. Create a RealmAwareZkClient with a valid sharding key - * -> This should pass - */ - @Test - public void testRealmAwareZkClientCreation() { - // Create a RealmAwareZkClient - String invalidShardingKey = "InvalidShardingKey"; - RealmAwareZkClient.RealmAwareZkClientConfig clientConfig = - new RealmAwareZkClient.RealmAwareZkClientConfig(); - - // Create a connection config with the invalid sharding key - RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder builder = - new RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder(); - RealmAwareZkClient.RealmAwareZkConnectionConfig connectionConfig = - builder.setZkRealmShardingKey(invalidShardingKey).build(); - - try { - _realmAwareZkClient = _realmAwareZkClientFactory - .buildZkClient(connectionConfig, clientConfig, _metadataStoreRoutingData); - Assert.fail("Should not succeed with an invalid sharding key!"); - } catch (IllegalArgumentException e) { - // Expected + if (_msdsServer != null) { + _msdsServer.stopServer(); } - - // Use a valid sharding key this time around - String validShardingKey = ZK_SHARDING_KEY_PREFIX + "_" + 0; // Use TEST_SHARDING_KEY_0 - builder.setZkRealmShardingKey(validShardingKey); - connectionConfig = builder.build(); - _realmAwareZkClient = _realmAwareZkClientFactory - .buildZkClient(connectionConfig, clientConfig, _metadataStoreRoutingData); - } - - /** - * Test the persistent create() call against a valid path and an invalid path. - * Valid path is one that belongs to the realm designated by the sharding key. - * Invalid path is one that does not belong to the realm designated by the sharding key. - */ - @Test(dependsOnMethods = "testRealmAwareZkClientCreation") - public void testRealmAwareZkClientCreatePersistent() { - _realmAwareZkClient.setZkSerializer(new ZNRecordSerializer()); - - // Create a dummy ZNRecord - ZNRecord znRecord = new ZNRecord("DummyRecord"); - znRecord.setSimpleField("Dummy", "Value"); - - // Test writing and reading against the validPath - _realmAwareZkClient.createPersistent(TEST_VALID_PATH, true); - _realmAwareZkClient.writeData(TEST_VALID_PATH, znRecord); - Assert.assertEquals(_realmAwareZkClient.readData(TEST_VALID_PATH), znRecord); - - // Test writing and reading against the invalid path - try { - _realmAwareZkClient.createPersistent(TEST_INVALID_PATH, true); - Assert.fail("Create() should not succeed on an invalid path!"); - } catch (IllegalArgumentException e) { - // Okay - expected - } - } - - /** - * Test that exists() works on valid path and fails on invalid path. - */ - @Test(dependsOnMethods = "testRealmAwareZkClientCreatePersistent") - public void testExists() { - Assert.assertTrue(_realmAwareZkClient.exists(TEST_VALID_PATH)); - - try { - _realmAwareZkClient.exists(TEST_INVALID_PATH); - Assert.fail("Exists() should not succeed on an invalid path!"); - } catch (IllegalArgumentException e) { - // Okay - expected - } - } - - /** - * Test that delete() works on valid path and fails on invalid path. - */ - @Test(dependsOnMethods = "testExists") - public void testDelete() { - try { - _realmAwareZkClient.delete(TEST_INVALID_PATH); - Assert.fail("Exists() should not succeed on an invalid path!"); - } catch (IllegalArgumentException e) { - // Okay - expected - } - - Assert.assertTrue(_realmAwareZkClient.delete(TEST_VALID_PATH)); - Assert.assertFalse(_realmAwareZkClient.exists(TEST_VALID_PATH)); } } \ No newline at end of file diff --git a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestDedicatedZkClient.java b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestDedicatedZkClient.java index 8cf3f85..fe8d8bd 100644 --- a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestDedicatedZkClient.java +++ b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestDedicatedZkClient.java @@ -19,15 +19,17 @@ package org.apache.helix.zookeeper.impl.client; * under the License. */ +import java.io.IOException; + +import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; import org.apache.helix.zookeeper.impl.factory.DedicatedZkClientFactory; import org.testng.annotations.BeforeClass; -public class TestDedicatedZkClient extends RealmAwareZkClientTestBase { +public class TestDedicatedZkClient extends RealmAwareZkClientFactoryTestBase { @BeforeClass - public void beforeClass() - throws Exception { + public void beforeClass() throws IOException, InvalidRoutingDataException { super.beforeClass(); // Set the factory to DedicatedZkClientFactory _realmAwareZkClientFactory = DedicatedZkClientFactory.getInstance(); diff --git a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestFederatedZkClient.java b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestFederatedZkClient.java index 5801690..0472df7 100644 --- a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestFederatedZkClient.java +++ b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestFederatedZkClient.java @@ -19,22 +19,17 @@ package org.apache.helix.zookeeper.impl.client; * under the License. */ +import java.io.IOException; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.TimeUnit; -import org.apache.helix.msdcommon.datamodel.TrieRoutingData; import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; import org.apache.helix.zookeeper.api.client.RealmAwareZkClient; import org.apache.helix.zookeeper.datamodel.ZNRecord; import org.apache.helix.zookeeper.datamodel.serializer.ZNRecordSerializer; -import org.apache.helix.zookeeper.impl.ZkTestBase; import org.apache.helix.zookeeper.zkclient.IZkStateListener; -import org.apache.helix.zookeeper.zkclient.ZkServer; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Op; import org.apache.zookeeper.Watcher; @@ -45,57 +40,33 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -public class TestFederatedZkClient extends ZkTestBase { - private static final String TEST_SHARDING_KEY_PREFIX = "/test_sharding_key_"; - private static final String TEST_REALM_ONE_VALID_PATH = TEST_SHARDING_KEY_PREFIX + "1/a/b/c"; - private static final String TEST_REALM_TWO_VALID_PATH = TEST_SHARDING_KEY_PREFIX + "2/x/y/z"; +public class TestFederatedZkClient extends RealmAwareZkClientTestBase { + private static final String TEST_SHARDING_KEY_PREFIX = ZK_SHARDING_KEY_PREFIX; + private static final String TEST_REALM_ONE_VALID_PATH = TEST_SHARDING_KEY_PREFIX + "/1/a/b/c"; + private static final String TEST_REALM_TWO_VALID_PATH = TEST_SHARDING_KEY_PREFIX + "/2/x/y/z"; private static final String TEST_INVALID_PATH = TEST_SHARDING_KEY_PREFIX + "invalid/a/b/c"; private static final String UNSUPPORTED_OPERATION_MESSAGE = "Session-aware operation is not supported by FederatedZkClient."; private RealmAwareZkClient _realmAwareZkClient; - // Need to start an extra ZK server for multi-realm test, if only one ZK server is running. - private String _extraZkRealm; - private ZkServer _extraZkServer; @BeforeClass - public void beforeClass() throws InvalidRoutingDataException { + public void beforeClass() throws IOException, InvalidRoutingDataException { System.out.println("Starting " + TestFederatedZkClient.class.getSimpleName()); - - // Populate rawRoutingData - // <Realm, List of sharding keys> Mapping - Map<String, List<String>> rawRoutingData = new HashMap<>(); - for (int i = 0; i < _numZk; i++) { - List<String> shardingKeyList = Collections.singletonList(TEST_SHARDING_KEY_PREFIX + (i + 1)); - String realmName = ZK_PREFIX + (ZK_START_PORT + i); - rawRoutingData.put(realmName, shardingKeyList); - } - - if (rawRoutingData.size() < 2) { - System.out.println("There is only one ZK realm. Starting one more ZK to test multi-realm."); - _extraZkRealm = ZK_PREFIX + (ZK_START_PORT + 1); - _extraZkServer = startZkServer(_extraZkRealm); - // RealmTwo's sharding key: /test_sharding_key_2 - List<String> shardingKeyList = Collections.singletonList(TEST_SHARDING_KEY_PREFIX + "2"); - rawRoutingData.put(_extraZkRealm, shardingKeyList); - } + super.beforeClass(); // Feed the raw routing data into TrieRoutingData to construct an in-memory representation // of routing information. - _realmAwareZkClient = new FederatedZkClient(new RealmAwareZkClient.RealmAwareZkClientConfig(), - new TrieRoutingData(rawRoutingData)); + _realmAwareZkClient = + new FederatedZkClient(new RealmAwareZkClient.RealmAwareZkConnectionConfig.Builder().build(), + new RealmAwareZkClient.RealmAwareZkClientConfig()); } @AfterClass public void afterClass() { + super.afterClass(); // Close it as it is created in before class. _realmAwareZkClient.close(); - - // Close the extra zk server. - if (_extraZkServer != null) { - _extraZkServer.shutdown(); - } - System.out.println("Ending " + TestFederatedZkClient.class.getSimpleName()); } @@ -103,7 +74,7 @@ public class TestFederatedZkClient extends ZkTestBase { * Tests that an unsupported operation should throw an UnsupportedOperationException. */ @Test - public void testUnsupportedOperations() { + public void testUnsupportedOperations() throws IOException, InvalidRoutingDataException { // Test creating ephemeral. try { _realmAwareZkClient.create(TEST_REALM_ONE_VALID_PATH, "Hello", CreateMode.EPHEMERAL); diff --git a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestSharedZkClient.java b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestSharedZkClient.java index 1dd44f6..08ec790 100644 --- a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestSharedZkClient.java +++ b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestSharedZkClient.java @@ -19,6 +19,9 @@ package org.apache.helix.zookeeper.impl.client; * under the License. */ +import java.io.IOException; + +import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; import org.apache.helix.zookeeper.datamodel.ZNRecord; import org.apache.helix.zookeeper.datamodel.serializer.ZNRecordSerializer; import org.apache.helix.zookeeper.impl.factory.SharedZkClientFactory; @@ -28,9 +31,10 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -public class TestSharedZkClient extends RealmAwareZkClientTestBase { +public class TestSharedZkClient extends RealmAwareZkClientFactoryTestBase { + @BeforeClass - public void beforeClass() throws Exception { + public void beforeClass() throws IOException, InvalidRoutingDataException { super.beforeClass(); // Set the factory to SharedZkClientFactory _realmAwareZkClientFactory = SharedZkClientFactory.getInstance(); diff --git a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/util/TestHttpRoutingDataReader.java b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/util/TestHttpRoutingDataReader.java index 1eccd1a..5c52d05 100644 --- a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/util/TestHttpRoutingDataReader.java +++ b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/util/TestHttpRoutingDataReader.java @@ -33,6 +33,7 @@ import org.apache.helix.msdcommon.constant.MetadataStoreRoutingConstants; import org.apache.helix.msdcommon.datamodel.MetadataStoreRoutingData; import org.apache.helix.msdcommon.exception.InvalidRoutingDataException; import org.apache.helix.msdcommon.mock.MockMetadataStoreDirectoryServer; +import org.apache.helix.zookeeper.constant.TestConstants; import org.apache.helix.zookeeper.impl.ZkTestBase; import org.testng.Assert; import org.testng.annotations.AfterClass; @@ -42,25 +43,15 @@ import org.testng.annotations.Test; public class TestHttpRoutingDataReader extends ZkTestBase { private MockMetadataStoreDirectoryServer _msdsServer; - private Map<String, Collection<String>> _testRawRoutingData; private final String _host = "localhost"; private final int _port = 1991; private final String _namespace = "TestHttpRoutingDataReader"; @BeforeClass public void beforeClass() throws IOException { - // Create fake routing data - _testRawRoutingData = new HashMap<>(); - _testRawRoutingData - .put("zk-0", ImmutableSet.of("/sharding-key-0", "/sharding-key-1", "/sharding-key-2")); - _testRawRoutingData - .put("zk-1", ImmutableSet.of("/sharding-key-3", "/sharding-key-4", "/sharding-key-5")); - _testRawRoutingData - .put("zk-2", ImmutableSet.of("/sharding-key-6", "/sharding-key-7", "/sharding-key-8")); - // Start MockMSDS - _msdsServer = - new MockMetadataStoreDirectoryServer(_host, _port, _namespace, _testRawRoutingData); + _msdsServer = new MockMetadataStoreDirectoryServer(_host, _port, _namespace, + TestConstants.FAKE_ROUTING_DATA); _msdsServer.startServer(); // Register the endpoint as a System property @@ -76,7 +67,7 @@ public class TestHttpRoutingDataReader extends ZkTestBase { @Test public void testGetRawRoutingData() throws IOException { Map<String, List<String>> rawRoutingData = HttpRoutingDataReader.getRawRoutingData(); - _testRawRoutingData.forEach((realm, keys) -> Assert + TestConstants.FAKE_ROUTING_DATA.forEach((realm, keys) -> Assert .assertEquals(new HashSet(rawRoutingData.get(realm)), new HashSet(keys))); } @@ -87,8 +78,10 @@ public class TestHttpRoutingDataReader extends ZkTestBase { Map<String, Set<String>> groupedMappings = allMappings.entrySet().stream().collect(Collectors .groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toSet()))); - _testRawRoutingData.forEach( - (realm, keys) -> Assert.assertEquals(groupedMappings.get(realm), new HashSet(keys))); + + TestConstants.FAKE_ROUTING_DATA.forEach((realm, keys) -> { + Assert.assertEquals(groupedMappings.get(realm), new HashSet(keys)); + }); } /** @@ -98,12 +91,12 @@ public class TestHttpRoutingDataReader extends ZkTestBase { public void testStaticMapping() throws IOException, InvalidRoutingDataException { // Modify routing data String newRealm = "newRealm"; - _testRawRoutingData.put(newRealm, ImmutableSet.of("/newKey")); + Map<String, Collection<String>> newRoutingData = new HashMap<>(TestConstants.FAKE_ROUTING_DATA); + newRoutingData.put(newRealm, ImmutableSet.of("/newKey")); // Kill MSDS and restart with a new mapping _msdsServer.stopServer(); - _msdsServer = - new MockMetadataStoreDirectoryServer(_host, _port, _namespace, _testRawRoutingData); + _msdsServer = new MockMetadataStoreDirectoryServer(_host, _port, _namespace, newRoutingData); _msdsServer.startServer(); // HttpRoutingDataReader should still return old data because it's static @@ -112,9 +105,9 @@ public class TestHttpRoutingDataReader extends ZkTestBase { Assert.assertFalse(rawRoutingData.containsKey(newRealm)); // Remove newRealm and check for equality - _testRawRoutingData.remove(newRealm); - Assert.assertEquals(rawRoutingData.keySet(), _testRawRoutingData.keySet()); - _testRawRoutingData.forEach((realm, keys) -> Assert + newRoutingData.remove(newRealm); + Assert.assertEquals(rawRoutingData.keySet(), TestConstants.FAKE_ROUTING_DATA.keySet()); + TestConstants.FAKE_ROUTING_DATA.forEach((realm, keys) -> Assert .assertEquals(new HashSet(rawRoutingData.get(realm)), new HashSet(keys))); MetadataStoreRoutingData data = HttpRoutingDataReader.getMetadataStoreRoutingData();
