You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by hu...@apache.org on 2020/04/08 22:54:01 UTC
[helix] 31/50: Implement setRoutingData for
MetadataStoreDirectoryService (#844)
This is an automated email from the ASF dual-hosted git repository.
hulee pushed a commit to branch zooscalability_merge
in repository https://gitbox.apache.org/repos/asf/helix.git
commit 6a21bafbd81d4346e7c67d29743b29c671be5179
Author: Neal Sun <ne...@gmail.com>
AuthorDate: Tue Mar 3 17:37:15 2020 -0800
Implement setRoutingData for MetadataStoreDirectoryService (#844)
Implement setRoutingData endpoint. Modify TrieRoutingData construction in MetadataStoreDirectory. Fix race conditions among writing operations in MetadataStoreDirectory.
---
.../rest/metadatastore/MetadataStoreDirectory.java | 9 ++
.../metadatastore/ZkMetadataStoreDirectory.java | 100 ++++++++++++++-----
.../accessor/MetadataStoreRoutingDataWriter.java | 2 +-
.../accessor/ZkRoutingDataReader.java | 2 +-
.../accessor/ZkRoutingDataWriter.java | 43 +++++---
.../MetadataStoreDirectoryAccessor.java | 25 +++++
.../TestZkMetadataStoreDirectory.java | 69 ++++++++++++-
.../accessor/TestZkRoutingDataReader.java | 64 +++++-------
.../accessor/TestZkRoutingDataWriter.java | 109 +++++++++++++--------
.../MetadataStoreDirectoryAccessorTestBase.java | 30 ++++--
.../rest/server/TestMSDAccessorLeaderElection.java | 57 ++++++++---
.../server/TestMetadataStoreDirectoryAccessor.java | 38 +++++++
.../helix/msdcommon/datamodel/TrieRoutingData.java | 4 +-
13 files changed, 409 insertions(+), 143 deletions(-)
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/MetadataStoreDirectory.java b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/MetadataStoreDirectory.java
index 4630d50..8371e07 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/MetadataStoreDirectory.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/MetadataStoreDirectory.java
@@ -61,6 +61,15 @@ public interface MetadataStoreDirectory extends AutoCloseable {
Map<String, List<String>> getNamespaceRoutingData(String namespace);
/**
+ * Sets and overwrites routing data in the given namespace.
+ *
+ * @param namespace namespace in metadata store directory.
+ * @param routingData Routing data map: realm -> List of sharding keys
+ * @return true if successful; false otherwise.
+ */
+ boolean setNamespaceRoutingData(String namespace, Map<String, List<String>> routingData);
+
+ /**
* Returns all path-based sharding keys in the given namespace and the realm.
* @param namespace
* @param realm
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/ZkMetadataStoreDirectory.java b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/ZkMetadataStoreDirectory.java
index 28a4afe..c83245f 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/ZkMetadataStoreDirectory.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/ZkMetadataStoreDirectory.java
@@ -113,10 +113,14 @@ public class ZkMetadataStoreDirectory implements MetadataStoreDirectory, Routing
_routingDataWriterMap.put(namespace, new ZkRoutingDataWriter(namespace, zkAddress));
// Populate realmToShardingKeys with ZkRoutingDataReader
- _realmToShardingKeysMap
- .put(namespace, _routingDataReaderMap.get(namespace).getRoutingData());
- _routingDataMap
- .put(namespace, new TrieRoutingData(_realmToShardingKeysMap.get(namespace)));
+ Map<String, List<String>> rawRoutingData =
+ _routingDataReaderMap.get(namespace).getRoutingData();
+ _realmToShardingKeysMap.put(namespace, rawRoutingData);
+ try {
+ _routingDataMap.put(namespace, new TrieRoutingData(rawRoutingData));
+ } catch (InvalidRoutingDataException e) {
+ LOG.warn("TrieRoutingData is not created for namespace {}", namespace, e);
+ }
}
}
}
@@ -156,6 +160,19 @@ public class ZkMetadataStoreDirectory implements MetadataStoreDirectory, Routing
}
@Override
+ public boolean setNamespaceRoutingData(String namespace, Map<String, List<String>> routingData) {
+ if (!_routingDataWriterMap.containsKey(namespace)) {
+ throw new IllegalArgumentException(
+ "Failed to set routing data: Namespace " + namespace + " is not found!");
+ }
+ synchronized (this) {
+ boolean result = _routingDataWriterMap.get(namespace).setRoutingData(routingData);
+ refreshRoutingData(namespace);
+ return result;
+ }
+ }
+
+ @Override
public Collection<String> getAllShardingKeysInRealm(String namespace, String realm) {
if (!_realmToShardingKeysMap.containsKey(namespace)) {
throw new NoSuchElementException("Namespace " + namespace + " does not exist!");
@@ -169,19 +186,31 @@ public class ZkMetadataStoreDirectory implements MetadataStoreDirectory, Routing
@Override
public Map<String, String> getAllMappingUnderPath(String namespace, String path) {
- if (!_routingDataMap.containsKey(namespace)) {
+ // Check _routingZkAddressMap first to see if namespace is included
+ if (!_routingZkAddressMap.containsKey(namespace)) {
throw new NoSuchElementException(
"Failed to get all mapping under path: Namespace " + namespace + " is not found!");
}
+ // If namespace is included but not routing data, it means the routing data is invalid
+ if (!_routingDataMap.containsKey(namespace)) {
+ throw new IllegalStateException("Failed to get all mapping under path: Namespace " + namespace
+ + " contains either empty or invalid routing data!");
+ }
return _routingDataMap.get(namespace).getAllMappingUnderPath(path);
}
@Override
public String getMetadataStoreRealm(String namespace, String shardingKey) {
- if (!_routingDataMap.containsKey(namespace)) {
+ // Check _routingZkAddressMap first to see if namespace is included
+ if (!_routingZkAddressMap.containsKey(namespace)) {
throw new NoSuchElementException(
"Failed to get metadata store realm: Namespace " + namespace + " is not found!");
}
+ // If namespace is included but not routing data, it means the routing data is invalid
+ if (!_routingDataMap.containsKey(namespace)) {
+ throw new IllegalStateException("Failed to get metadata store realm: Namespace " + namespace
+ + " contains either empty or invalid routing data!");
+ }
return _routingDataMap.get(namespace).getMetadataStoreRealm(shardingKey);
}
@@ -193,7 +222,11 @@ public class ZkMetadataStoreDirectory implements MetadataStoreDirectory, Routing
throw new NoSuchElementException(
"Failed to add metadata store realm: Namespace " + namespace + " is not found!");
}
- return _routingDataWriterMap.get(namespace).addMetadataStoreRealm(realm);
+ synchronized (this) {
+ boolean result = _routingDataWriterMap.get(namespace).addMetadataStoreRealm(realm);
+ refreshRoutingData(namespace);
+ return result;
+ }
}
@Override
@@ -204,26 +237,36 @@ public class ZkMetadataStoreDirectory implements MetadataStoreDirectory, Routing
throw new NoSuchElementException(
"Failed to delete metadata store realm: Namespace " + namespace + " is not found!");
}
- return _routingDataWriterMap.get(namespace).deleteMetadataStoreRealm(realm);
+ synchronized (this) {
+ boolean result = _routingDataWriterMap.get(namespace).deleteMetadataStoreRealm(realm);
+ refreshRoutingData(namespace);
+ return result;
+ }
}
@Override
public boolean addShardingKey(String namespace, String realm, String shardingKey) {
- if (!_routingDataWriterMap.containsKey(namespace) || !_routingDataMap.containsKey(namespace)) {
+ if (!_routingDataWriterMap.containsKey(namespace)) {
// throwing NoSuchElementException instead of IllegalArgumentException to differentiate the
// status code in the Accessor level
throw new NoSuchElementException(
"Failed to add sharding key: Namespace " + namespace + " is not found!");
}
- if (_routingDataMap.get(namespace).containsKeyRealmPair(shardingKey, realm)) {
- return true;
- }
- if (!_routingDataMap.get(namespace).isShardingKeyInsertionValid(shardingKey)) {
- throw new IllegalArgumentException(
- "Failed to add sharding key: Adding sharding key " + shardingKey
- + " makes routing data invalid!");
+ synchronized (this) {
+ if (_routingDataMap.containsKey(namespace) && _routingDataMap.get(namespace)
+ .containsKeyRealmPair(shardingKey, realm)) {
+ return true;
+ }
+ if (_routingDataMap.containsKey(namespace) && !_routingDataMap.get(namespace)
+ .isShardingKeyInsertionValid(shardingKey)) {
+ throw new IllegalArgumentException(
+ "Failed to add sharding key: Adding sharding key " + shardingKey
+ + " makes routing data invalid!");
+ }
+ boolean result = _routingDataWriterMap.get(namespace).addShardingKey(realm, shardingKey);
+ refreshRoutingData(namespace);
+ return result;
}
- return _routingDataWriterMap.get(namespace).addShardingKey(realm, shardingKey);
}
@Override
@@ -234,7 +277,11 @@ public class ZkMetadataStoreDirectory implements MetadataStoreDirectory, Routing
throw new NoSuchElementException(
"Failed to delete sharding key: Namespace " + namespace + " is not found!");
}
- return _routingDataWriterMap.get(namespace).deleteShardingKey(realm, shardingKey);
+ synchronized (this) {
+ boolean result = _routingDataWriterMap.get(namespace).deleteShardingKey(realm, shardingKey);
+ refreshRoutingData(namespace);
+ return result;
+ }
}
/**
@@ -251,7 +298,7 @@ public class ZkMetadataStoreDirectory implements MetadataStoreDirectory, Routing
// Safe to ignore the callback if any of the maps are null.
// If routingDataMap is null, then it will be populated by the constructor anyway
// If routingDataMap is not null, then it's safe for the callback function to update it
- if (_routingZkAddressMap == null || _routingDataMap == null || _realmToShardingKeysMap == null
+ if (_routingZkAddressMap == null || _realmToShardingKeysMap == null
|| _routingDataReaderMap == null || _routingDataWriterMap == null) {
LOG.warn(
"refreshRoutingData callback called before ZKMetadataStoreDirectory was fully initialized. Skipping refresh!");
@@ -262,17 +309,22 @@ public class ZkMetadataStoreDirectory implements MetadataStoreDirectory, Routing
if (!_routingZkAddressMap.containsKey(namespace)) {
LOG.error(
"Failed to refresh internally-cached routing data! Namespace not found: " + namespace);
+ return;
}
+ Map<String, List<String>> rawRoutingData;
try {
- Map<String, List<String>> rawRoutingData =
- _routingDataReaderMap.get(namespace).getRoutingData();
+ rawRoutingData = _routingDataReaderMap.get(namespace).getRoutingData();
_realmToShardingKeysMap.put(namespace, rawRoutingData);
-
- MetadataStoreRoutingData routingData = new TrieRoutingData(rawRoutingData);
- _routingDataMap.put(namespace, routingData);
} catch (InvalidRoutingDataException e) {
LOG.error("Failed to refresh cached routing data for namespace {}", namespace, e);
+ return;
+ }
+
+ try {
+ _routingDataMap.put(namespace, new TrieRoutingData(rawRoutingData));
+ } catch (InvalidRoutingDataException e) {
+ LOG.warn("TrieRoutingData is not created for namespace {}", namespace, e);
}
}
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/MetadataStoreRoutingDataWriter.java b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/MetadataStoreRoutingDataWriter.java
index 349bbd0..02ce60b 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/MetadataStoreRoutingDataWriter.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/MetadataStoreRoutingDataWriter.java
@@ -63,7 +63,7 @@ public interface MetadataStoreRoutingDataWriter {
* Sets (overwrites) the routing data with the given <realm, list of sharding keys> mapping.
* WARNING: This overwrites all existing routing data. Use with care!
* @param routingData
- * @return
+ * @return true if successful; false otherwise.
*/
boolean setRoutingData(Map<String, List<String>> routingData);
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/ZkRoutingDataReader.java b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/ZkRoutingDataReader.java
index 9251571..6c75618 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/ZkRoutingDataReader.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/ZkRoutingDataReader.java
@@ -99,7 +99,7 @@ public class ZkRoutingDataReader implements MetadataStoreRoutingDataReader, IZkD
if (allRealmAddresses != null) {
for (String realmAddress : allRealmAddresses) {
ZNRecord record = _zkClient
- .readData(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + realmAddress);
+ .readData(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + realmAddress, true);
if (record != null) {
List<String> shardingKeys =
record.getListField(MetadataStoreRoutingConstants.ZNRECORD_LIST_FIELD_KEY);
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/ZkRoutingDataWriter.java b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/ZkRoutingDataWriter.java
index 74cc14c..fddd9ee 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/ZkRoutingDataWriter.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/accessor/ZkRoutingDataWriter.java
@@ -41,9 +41,14 @@ import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,6 +57,7 @@ public class ZkRoutingDataWriter implements MetadataStoreRoutingDataWriter {
// Time out for http requests that are forwarded to leader instances measured in milliseconds
private static final int HTTP_REQUEST_FORWARDING_TIMEOUT = 60 * 1000;
private static final Logger LOG = LoggerFactory.getLogger(ZkRoutingDataWriter.class);
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private final String _namespace;
private final HelixZkClient _zkClient;
@@ -113,7 +119,7 @@ public class ZkRoutingDataWriter implements MetadataStoreRoutingDataWriter {
String urlSuffix =
constructUrlSuffix(MetadataStoreRoutingConstants.MSDS_GET_ALL_REALMS_ENDPOINT, realm);
- return forwardRequestToLeader(urlSuffix, HttpConstants.RestVerbs.PUT,
+ return buildAndSendRequestToLeader(urlSuffix, HttpConstants.RestVerbs.PUT,
Response.Status.CREATED.getStatusCode());
}
@@ -128,7 +134,7 @@ public class ZkRoutingDataWriter implements MetadataStoreRoutingDataWriter {
String urlSuffix =
constructUrlSuffix(MetadataStoreRoutingConstants.MSDS_GET_ALL_REALMS_ENDPOINT, realm);
- return forwardRequestToLeader(urlSuffix, HttpConstants.RestVerbs.DELETE,
+ return buildAndSendRequestToLeader(urlSuffix, HttpConstants.RestVerbs.DELETE,
Response.Status.OK.getStatusCode());
}
@@ -144,7 +150,7 @@ public class ZkRoutingDataWriter implements MetadataStoreRoutingDataWriter {
String urlSuffix =
constructUrlSuffix(MetadataStoreRoutingConstants.MSDS_GET_ALL_REALMS_ENDPOINT, realm,
MetadataStoreRoutingConstants.MSDS_GET_ALL_SHARDING_KEYS_ENDPOINT, shardingKey);
- return forwardRequestToLeader(urlSuffix, HttpConstants.RestVerbs.PUT,
+ return buildAndSendRequestToLeader(urlSuffix, HttpConstants.RestVerbs.PUT,
Response.Status.CREATED.getStatusCode());
}
@@ -160,7 +166,7 @@ public class ZkRoutingDataWriter implements MetadataStoreRoutingDataWriter {
String urlSuffix =
constructUrlSuffix(MetadataStoreRoutingConstants.MSDS_GET_ALL_REALMS_ENDPOINT, realm,
MetadataStoreRoutingConstants.MSDS_GET_ALL_SHARDING_KEYS_ENDPOINT, shardingKey);
- return forwardRequestToLeader(urlSuffix, HttpConstants.RestVerbs.DELETE,
+ return buildAndSendRequestToLeader(urlSuffix, HttpConstants.RestVerbs.DELETE,
Response.Status.OK.getStatusCode());
}
@@ -209,8 +215,23 @@ public class ZkRoutingDataWriter implements MetadataStoreRoutingDataWriter {
return true;
}
- // TODO: Forward the request to leader
- return true;
+ String leaderHostName = _leaderElection.getCurrentLeaderInfo().getId();
+ String url = leaderHostName + constructUrlSuffix(
+ MetadataStoreRoutingConstants.MSDS_GET_ALL_ROUTING_DATA_ENDPOINT);
+ HttpPut httpPut = new HttpPut(url);
+ String routingDataJsonString;
+ try {
+ routingDataJsonString = OBJECT_MAPPER.writeValueAsString(routingData);
+ } catch (JsonGenerationException | JsonMappingException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ } catch (IOException e) {
+ LOG.error(
+ "setRoutingData failed before forwarding the request to leader: an exception happened while routingData is converted to json. routingData: {}",
+ routingData, e);
+ return false;
+ }
+ httpPut.setEntity(new StringEntity(routingDataJsonString, ContentType.APPLICATION_JSON));
+ return sendRequestToLeader(httpPut, Response.Status.CREATED.getStatusCode(), leaderHostName);
}
@Override
@@ -332,12 +353,13 @@ public class ZkRoutingDataWriter implements MetadataStoreRoutingDataWriter {
return String.join("", allUrlParameters);
}
- private boolean forwardRequestToLeader(String urlSuffix, HttpConstants.RestVerbs request_method,
- int expectedResponseCode) throws IllegalArgumentException {
+ private boolean buildAndSendRequestToLeader(String urlSuffix,
+ HttpConstants.RestVerbs requestMethod, int expectedResponseCode)
+ throws IllegalArgumentException {
String leaderHostName = _leaderElection.getCurrentLeaderInfo().getId();
String url = leaderHostName + urlSuffix;
HttpUriRequest request;
- switch (request_method) {
+ switch (requestMethod) {
case PUT:
request = new HttpPut(url);
break;
@@ -345,8 +367,7 @@ public class ZkRoutingDataWriter implements MetadataStoreRoutingDataWriter {
request = new HttpDelete(url);
break;
default:
- LOG.error("Unsupported request_method: " + request_method.name());
- return false;
+ throw new IllegalArgumentException("Unsupported requestMethod: " + requestMethod.name());
}
return sendRequestToLeader(request, expectedResponseCode, leaderHostName);
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadatastore/MetadataStoreDirectoryAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadatastore/MetadataStoreDirectoryAccessor.java
index 0763ec1..f20fd9a 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadatastore/MetadataStoreDirectoryAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/metadatastore/MetadataStoreDirectoryAccessor.java
@@ -19,18 +19,22 @@ package org.apache.helix.rest.server.resources.metadatastore;
* under the License.
*/
+import java.io.IOException;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
+import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.google.common.collect.ImmutableMap;
@@ -44,6 +48,9 @@ import org.apache.helix.rest.metadatastore.ZkMetadataStoreDirectory;
import org.apache.helix.rest.metadatastore.datamodel.MetadataStoreShardingKey;
import org.apache.helix.rest.metadatastore.datamodel.MetadataStoreShardingKeysByRealm;
import org.apache.helix.rest.server.resources.AbstractResource;
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.type.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -220,6 +227,24 @@ public class MetadataStoreDirectoryAccessor extends AbstractResource {
return JSONRepresentation(responseMap);
}
+ @PUT
+ @Path("/routing-data")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response setRoutingData(String jsonContent) {
+ try {
+ Map<String, List<String>> routingData =
+ OBJECT_MAPPER.readValue(jsonContent, new TypeReference<HashMap<String, List<String>>>() {
+ });
+ _metadataStoreDirectory.setNamespaceRoutingData(_namespace, routingData);
+ } catch (JsonMappingException | JsonParseException | IllegalArgumentException e) {
+ return badRequest(e.getMessage());
+ } catch (IOException e) {
+ return serverError(e);
+ }
+
+ return created();
+ }
+
/**
* Gets all path-based sharding keys for a queried realm at endpoint:
* "GET /metadata-store-realms/{realm}/sharding-keys"
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/TestZkMetadataStoreDirectory.java b/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/TestZkMetadataStoreDirectory.java
index 8eddcea..df84754 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/TestZkMetadataStoreDirectory.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/TestZkMetadataStoreDirectory.java
@@ -35,6 +35,7 @@ import org.apache.helix.msdcommon.exception.InvalidRoutingDataException;
import org.apache.helix.rest.server.AbstractTestClass;
import org.apache.helix.zookeeper.datamodel.ZNRecord;
import org.apache.helix.zookeeper.datamodel.serializer.ZNRecordSerializer;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@@ -62,9 +63,11 @@ public class TestZkMetadataStoreDirectory extends AbstractTestClass {
private MetadataStoreDirectory _metadataStoreDirectory;
@BeforeClass
- public void beforeClass() throws InvalidRoutingDataException {
+ public void beforeClass() throws Exception {
_zkList = new ArrayList<>(ZK_SERVER_MAP.keySet());
+ clearRoutingData();
+
// Populate routingZkAddrMap
_routingZkAddrMap = new LinkedHashMap<>();
int namespaceIndex = 0;
@@ -111,10 +114,9 @@ public class TestZkMetadataStoreDirectory extends AbstractTestClass {
}
@AfterClass
- public void afterClass() {
+ public void afterClass() throws Exception {
_metadataStoreDirectory.close();
- _zkList.forEach(zk -> ZK_SERVER_MAP.get(zk).getZkClient()
- .deleteRecursive(MetadataStoreRoutingConstants.ROUTING_DATA_PATH));
+ clearRoutingData();
System.clearProperty(MetadataStoreRoutingConstants.MSDS_SERVER_HOSTNAME_KEY);
}
@@ -147,6 +149,42 @@ public class TestZkMetadataStoreDirectory extends AbstractTestClass {
}
@Test(dependsOnMethods = "testGetAllShardingKeys")
+ public void testGetNamespaceRoutingData() {
+ Map<String, List<String>> routingDataMap = new HashMap<>();
+ routingDataMap.put(TEST_REALM_1, TEST_SHARDING_KEYS_1);
+ routingDataMap.put(TEST_REALM_2, TEST_SHARDING_KEYS_2);
+
+ for (String namespace : _routingZkAddrMap.keySet()) {
+ Assert
+ .assertEquals(_metadataStoreDirectory.getNamespaceRoutingData(namespace), routingDataMap);
+ }
+ }
+
+ @Test(dependsOnMethods = "testGetNamespaceRoutingData")
+ public void testSetNamespaceRoutingData() {
+ Map<String, List<String>> routingDataMap = new HashMap<>();
+ routingDataMap.put(TEST_REALM_1, TEST_SHARDING_KEYS_2);
+ routingDataMap.put(TEST_REALM_2, TEST_SHARDING_KEYS_1);
+
+ for (String namespace : _routingZkAddrMap.keySet()) {
+ _metadataStoreDirectory.setNamespaceRoutingData(namespace, routingDataMap);
+ Assert
+ .assertEquals(_metadataStoreDirectory.getNamespaceRoutingData(namespace), routingDataMap);
+ }
+
+ // Revert it back to the original state
+ Map<String, List<String>> originalRoutingDataMap = new HashMap<>();
+ originalRoutingDataMap.put(TEST_REALM_1, TEST_SHARDING_KEYS_1);
+ originalRoutingDataMap.put(TEST_REALM_2, TEST_SHARDING_KEYS_2);
+
+ for (String namespace : _routingZkAddrMap.keySet()) {
+ _metadataStoreDirectory.setNamespaceRoutingData(namespace, originalRoutingDataMap);
+ Assert.assertEquals(_metadataStoreDirectory.getNamespaceRoutingData(namespace),
+ originalRoutingDataMap);
+ }
+ }
+
+ @Test(dependsOnMethods = "testGetNamespaceRoutingData")
public void testGetAllShardingKeysInRealm() {
for (String namespace : _routingZkAddrMap.keySet()) {
// Test two realms independently
@@ -277,4 +315,27 @@ public class TestZkMetadataStoreDirectory extends AbstractTestClass {
return false;
}, TestHelper.WAIT_DURATION));
}
+
+ private void clearRoutingData() throws Exception {
+ Assert.assertTrue(TestHelper.verify(() -> {
+ for (String zk : _zkList) {
+ ZkClient zkClient = ZK_SERVER_MAP.get(zk).getZkClient();
+ if (zkClient.exists(MetadataStoreRoutingConstants.ROUTING_DATA_PATH)) {
+ for (String zkRealm : zkClient
+ .getChildren(MetadataStoreRoutingConstants.ROUTING_DATA_PATH)) {
+ zkClient.delete(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + zkRealm);
+ }
+ }
+ }
+
+ for (String zk : _zkList) {
+ ZkClient zkClient = ZK_SERVER_MAP.get(zk).getZkClient();
+ if (zkClient.exists(MetadataStoreRoutingConstants.ROUTING_DATA_PATH) && !zkClient
+ .getChildren(MetadataStoreRoutingConstants.ROUTING_DATA_PATH).isEmpty()) {
+ return false;
+ }
+ }
+ return true;
+ }, TestHelper.WAIT_DURATION), "Routing data path should be deleted after the tests.");
+ }
}
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/accessor/TestZkRoutingDataReader.java b/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/accessor/TestZkRoutingDataReader.java
index 63a013b..4c95445 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/accessor/TestZkRoutingDataReader.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/accessor/TestZkRoutingDataReader.java
@@ -24,12 +24,12 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
-import org.apache.helix.AccessOption;
import org.apache.helix.TestHelper;
import org.apache.helix.msdcommon.constant.MetadataStoreRoutingConstants;
import org.apache.helix.msdcommon.exception.InvalidRoutingDataException;
import org.apache.helix.rest.server.AbstractTestClass;
import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
@@ -38,24 +38,25 @@ import org.testng.annotations.Test;
public class TestZkRoutingDataReader extends AbstractTestClass {
- private static final String DUMMY_NAMESPACE = "NAMESPACE";
private MetadataStoreRoutingDataReader _zkRoutingDataReader;
+ private ZkClient _zkClient;
@BeforeClass
public void beforeClass() throws Exception {
- deleteRoutingDataPath();
- _zkRoutingDataReader = new ZkRoutingDataReader(DUMMY_NAMESPACE, ZK_ADDR, null);
+ _zkClient = ZK_SERVER_MAP.get(_zkAddrTestNS).getZkClient();
+ _zkRoutingDataReader = new ZkRoutingDataReader(TEST_NAMESPACE, _zkAddrTestNS, null);
+ clearRoutingDataPath();
}
@AfterClass
public void afterClass() throws Exception {
_zkRoutingDataReader.close();
- deleteRoutingDataPath();
+ clearRoutingDataPath();
}
@AfterMethod
- public void afterMethod() {
- _baseAccessor.remove(MetadataStoreRoutingConstants.ROUTING_DATA_PATH, AccessOption.PERSISTENT);
+ public void afterMethod() throws Exception {
+ clearRoutingDataPath();
}
@Test
@@ -75,10 +76,14 @@ public class TestZkRoutingDataReader extends AbstractTestClass {
.setListField(MetadataStoreRoutingConstants.ZNRECORD_LIST_FIELD_KEY, testShardingKeys2);
// Add both nodes as children nodes to ZkRoutingDataReader.ROUTING_DATA_PATH
- _baseAccessor.create(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/testRealmAddress1",
- testZnRecord1, AccessOption.PERSISTENT);
- _baseAccessor.create(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/testRealmAddress2",
- testZnRecord2, AccessOption.PERSISTENT);
+ _zkClient
+ .createPersistent(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/testRealmAddress1");
+ _zkClient.writeData(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/testRealmAddress1",
+ testZnRecord1);
+ _zkClient
+ .createPersistent(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/testRealmAddress2");
+ _zkClient.writeData(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/testRealmAddress2",
+ testZnRecord2);
try {
Map<String, List<String>> routingData = _zkRoutingDataReader.getRoutingData();
@@ -90,22 +95,8 @@ public class TestZkRoutingDataReader extends AbstractTestClass {
}
}
- @Test
- public void testGetRoutingDataMissingMSRD() {
- try {
- _zkRoutingDataReader.getRoutingData();
- Assert.fail("Expecting InvalidRoutingDataException");
- } catch (InvalidRoutingDataException e) {
- Assert.assertTrue(e.getMessage().contains(
- "Routing data directory ZNode " + MetadataStoreRoutingConstants.ROUTING_DATA_PATH
- + " does not exist. Routing ZooKeeper address: " + ZK_ADDR));
- }
- }
-
- @Test
+ @Test(dependsOnMethods = "testGetRoutingData")
public void testGetRoutingDataMissingMSRDChildren() {
- _baseAccessor.create(MetadataStoreRoutingConstants.ROUTING_DATA_PATH, new ZNRecord("test"),
- AccessOption.PERSISTENT);
try {
Map<String, List<String>> routingData = _zkRoutingDataReader.getRoutingData();
Assert.assertEquals(routingData.size(), 0);
@@ -114,13 +105,15 @@ public class TestZkRoutingDataReader extends AbstractTestClass {
}
}
- @Test
+ @Test(dependsOnMethods = "testGetRoutingData")
public void testGetRoutingDataMSRDChildEmptyValue() {
ZNRecord testZnRecord1 = new ZNRecord("testZnRecord1");
testZnRecord1.setListField(MetadataStoreRoutingConstants.ZNRECORD_LIST_FIELD_KEY,
Collections.emptyList());
- _baseAccessor.create(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/testRealmAddress1",
- testZnRecord1, AccessOption.PERSISTENT);
+ _zkClient
+ .createPersistent(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/testRealmAddress1");
+ _zkClient.writeData(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/testRealmAddress1",
+ testZnRecord1);
try {
Map<String, List<String>> routingData = _zkRoutingDataReader.getRoutingData();
Assert.assertEquals(routingData.size(), 1);
@@ -130,17 +123,14 @@ public class TestZkRoutingDataReader extends AbstractTestClass {
}
}
- private void deleteRoutingDataPath() throws Exception {
+ private void clearRoutingDataPath() throws Exception {
Assert.assertTrue(TestHelper.verify(() -> {
- ZK_SERVER_MAP.get(ZK_ADDR).getZkClient()
- .deleteRecursively(MetadataStoreRoutingConstants.ROUTING_DATA_PATH);
-
- if (ZK_SERVER_MAP.get(ZK_ADDR).getZkClient()
- .exists(MetadataStoreRoutingConstants.ROUTING_DATA_PATH)) {
- return false;
+ for (String zkRealm : _zkClient
+ .getChildren(MetadataStoreRoutingConstants.ROUTING_DATA_PATH)) {
+ _zkClient.delete(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + zkRealm);
}
- return true;
+ return _zkClient.getChildren(MetadataStoreRoutingConstants.ROUTING_DATA_PATH).isEmpty();
}, TestHelper.WAIT_DURATION), "Routing data path should be deleted after the tests.");
}
}
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/accessor/TestZkRoutingDataWriter.java b/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/accessor/TestZkRoutingDataWriter.java
index 069931f..31db291 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/accessor/TestZkRoutingDataWriter.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/accessor/TestZkRoutingDataWriter.java
@@ -25,22 +25,25 @@ import java.util.List;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
-import org.apache.helix.AccessOption;
+import org.apache.helix.TestHelper;
import org.apache.helix.msdcommon.constant.MetadataStoreRoutingConstants;
+import org.apache.helix.rest.common.HttpConstants;
import org.apache.helix.rest.server.AbstractTestClass;
import org.apache.helix.zookeeper.datamodel.ZNRecord;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
import org.apache.http.client.methods.HttpUriRequest;
-import org.junit.Assert;
+import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class TestZkRoutingDataWriter extends AbstractTestClass {
- private static final String DUMMY_NAMESPACE = "NAMESPACE";
private static final String DUMMY_REALM = "REALM";
- private static final String DUMMY_SHARDING_KEY = "SHARDING_KEY";
+ private static final String DUMMY_SHARDING_KEY = "/DUMMY/SHARDING/KEY";
+
private MetadataStoreRoutingDataWriter _zkRoutingDataWriter;
+ private ZkClient _zkClient;
// MockWriter is used for testing request forwarding features in non-leader situations
class MockWriter extends ZkRoutingDataWriter {
@@ -60,43 +63,41 @@ public class TestZkRoutingDataWriter extends AbstractTestClass {
}
@BeforeClass
- public void beforeClass() {
- _baseAccessor.remove(MetadataStoreRoutingConstants.ROUTING_DATA_PATH, AccessOption.PERSISTENT);
+ public void beforeClass() throws Exception {
+ _zkClient = ZK_SERVER_MAP.get(_zkAddrTestNS).getZkClient();
System.setProperty(MetadataStoreRoutingConstants.MSDS_SERVER_HOSTNAME_KEY,
getBaseUri().getHost() + ":" + getBaseUri().getPort());
- _zkRoutingDataWriter = new ZkRoutingDataWriter(DUMMY_NAMESPACE, ZK_ADDR);
+ _zkRoutingDataWriter = new ZkRoutingDataWriter(TEST_NAMESPACE, _zkAddrTestNS);
+ clearRoutingDataPath();
}
@AfterClass
- public void afterClass() {
- _baseAccessor.remove(MetadataStoreRoutingConstants.ROUTING_DATA_PATH, AccessOption.PERSISTENT);
+ public void afterClass() throws Exception {
System.clearProperty(MetadataStoreRoutingConstants.MSDS_SERVER_HOSTNAME_KEY);
_zkRoutingDataWriter.close();
+ clearRoutingDataPath();
}
@Test
public void testAddMetadataStoreRealm() {
_zkRoutingDataWriter.addMetadataStoreRealm(DUMMY_REALM);
- ZNRecord znRecord = _baseAccessor
- .get(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + DUMMY_REALM, null,
- AccessOption.PERSISTENT);
+ ZNRecord znRecord =
+ _zkClient.readData(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + DUMMY_REALM);
Assert.assertNotNull(znRecord);
}
@Test(dependsOnMethods = "testAddMetadataStoreRealm")
public void testDeleteMetadataStoreRealm() {
_zkRoutingDataWriter.deleteMetadataStoreRealm(DUMMY_REALM);
- Assert.assertFalse(_baseAccessor
- .exists(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + DUMMY_REALM,
- AccessOption.PERSISTENT));
+ Assert.assertFalse(
+ _zkClient.exists(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + DUMMY_REALM));
}
@Test(dependsOnMethods = "testDeleteMetadataStoreRealm")
public void testAddShardingKey() {
_zkRoutingDataWriter.addShardingKey(DUMMY_REALM, DUMMY_SHARDING_KEY);
- ZNRecord znRecord = _baseAccessor
- .get(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + DUMMY_REALM, null,
- AccessOption.PERSISTENT);
+ ZNRecord znRecord =
+ _zkClient.readData(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + DUMMY_REALM);
Assert.assertNotNull(znRecord);
Assert.assertTrue(znRecord.getListField(MetadataStoreRoutingConstants.ZNRECORD_LIST_FIELD_KEY)
.contains(DUMMY_SHARDING_KEY));
@@ -105,9 +106,8 @@ public class TestZkRoutingDataWriter extends AbstractTestClass {
@Test(dependsOnMethods = "testAddShardingKey")
public void testDeleteShardingKey() {
_zkRoutingDataWriter.deleteShardingKey(DUMMY_REALM, DUMMY_SHARDING_KEY);
- ZNRecord znRecord = _baseAccessor
- .get(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + DUMMY_REALM, null,
- AccessOption.PERSISTENT);
+ ZNRecord znRecord =
+ _zkClient.readData(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + DUMMY_REALM);
Assert.assertNotNull(znRecord);
Assert.assertFalse(znRecord.getListField(MetadataStoreRoutingConstants.ZNRECORD_LIST_FIELD_KEY)
.contains(DUMMY_SHARDING_KEY));
@@ -118,9 +118,8 @@ public class TestZkRoutingDataWriter extends AbstractTestClass {
Map<String, List<String>> testRoutingDataMap =
ImmutableMap.of(DUMMY_REALM, Collections.singletonList(DUMMY_SHARDING_KEY));
_zkRoutingDataWriter.setRoutingData(testRoutingDataMap);
- ZNRecord znRecord = _baseAccessor
- .get(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + DUMMY_REALM, null,
- AccessOption.PERSISTENT);
+ ZNRecord znRecord =
+ _zkClient.readData(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + DUMMY_REALM);
Assert.assertNotNull(znRecord);
Assert.assertEquals(
znRecord.getListField(MetadataStoreRoutingConstants.ZNRECORD_LIST_FIELD_KEY).size(), 1);
@@ -130,63 +129,93 @@ public class TestZkRoutingDataWriter extends AbstractTestClass {
@Test(dependsOnMethods = "testSetRoutingData")
public void testAddMetadataStoreRealmNonLeader() {
- MockWriter mockWriter = new MockWriter(DUMMY_NAMESPACE, ZK_ADDR);
+ MockWriter mockWriter = new MockWriter(TEST_NAMESPACE, _zkAddrTestNS);
mockWriter.addMetadataStoreRealm(DUMMY_REALM);
- Assert.assertEquals("PUT", mockWriter.calledRequest.getMethod());
+ Assert.assertEquals(mockWriter.calledRequest.getMethod(), HttpConstants.RestVerbs.PUT.name());
List<String> expectedUrlParams = Arrays
- .asList(MetadataStoreRoutingConstants.MSDS_NAMESPACES_URL_PREFIX, DUMMY_NAMESPACE,
+ .asList(MetadataStoreRoutingConstants.MSDS_NAMESPACES_URL_PREFIX, TEST_NAMESPACE,
MetadataStoreRoutingConstants.MSDS_GET_ALL_REALMS_ENDPOINT, DUMMY_REALM);
String expectedUrl =
getBaseUri().toString() + String.join("/", expectedUrlParams).replaceAll("//", "/")
.substring(1);
- Assert.assertEquals(expectedUrl, mockWriter.calledRequest.getURI().toString());
+ Assert.assertEquals(mockWriter.calledRequest.getURI().toString(), expectedUrl);
mockWriter.close();
}
@Test(dependsOnMethods = "testAddMetadataStoreRealmNonLeader")
public void testDeleteMetadataStoreRealmNonLeader() {
- MockWriter mockWriter = new MockWriter(DUMMY_NAMESPACE, ZK_ADDR);
+ MockWriter mockWriter = new MockWriter(TEST_NAMESPACE, _zkAddrTestNS);
mockWriter.deleteMetadataStoreRealm(DUMMY_REALM);
- Assert.assertEquals("DELETE", mockWriter.calledRequest.getMethod());
+ Assert
+ .assertEquals(mockWriter.calledRequest.getMethod(), HttpConstants.RestVerbs.DELETE.name());
List<String> expectedUrlParams = Arrays
- .asList(MetadataStoreRoutingConstants.MSDS_NAMESPACES_URL_PREFIX, DUMMY_NAMESPACE,
+ .asList(MetadataStoreRoutingConstants.MSDS_NAMESPACES_URL_PREFIX, TEST_NAMESPACE,
MetadataStoreRoutingConstants.MSDS_GET_ALL_REALMS_ENDPOINT, DUMMY_REALM);
String expectedUrl =
getBaseUri().toString() + String.join("/", expectedUrlParams).replaceAll("//", "/")
.substring(1);
- Assert.assertEquals(expectedUrl, mockWriter.calledRequest.getURI().toString());
+ Assert.assertEquals(mockWriter.calledRequest.getURI().toString(), expectedUrl);
mockWriter.close();
}
@Test(dependsOnMethods = "testDeleteMetadataStoreRealmNonLeader")
public void testAddShardingKeyNonLeader() {
- MockWriter mockWriter = new MockWriter(DUMMY_NAMESPACE, ZK_ADDR);
+ MockWriter mockWriter = new MockWriter(TEST_NAMESPACE, _zkAddrTestNS);
mockWriter.addShardingKey(DUMMY_REALM, DUMMY_SHARDING_KEY);
- Assert.assertEquals("PUT", mockWriter.calledRequest.getMethod());
+ Assert.assertEquals(mockWriter.calledRequest.getMethod(), HttpConstants.RestVerbs.PUT.name());
List<String> expectedUrlParams = Arrays
- .asList(MetadataStoreRoutingConstants.MSDS_NAMESPACES_URL_PREFIX, DUMMY_NAMESPACE,
+ .asList(MetadataStoreRoutingConstants.MSDS_NAMESPACES_URL_PREFIX, TEST_NAMESPACE,
MetadataStoreRoutingConstants.MSDS_GET_ALL_REALMS_ENDPOINT, DUMMY_REALM,
MetadataStoreRoutingConstants.MSDS_GET_ALL_SHARDING_KEYS_ENDPOINT, DUMMY_SHARDING_KEY);
String expectedUrl =
getBaseUri().toString() + String.join("/", expectedUrlParams).replaceAll("//", "/")
.substring(1);
- Assert.assertEquals(expectedUrl, mockWriter.calledRequest.getURI().toString());
+ Assert.assertEquals(mockWriter.calledRequest.getURI().toString(), expectedUrl);
mockWriter.close();
}
@Test(dependsOnMethods = "testAddShardingKeyNonLeader")
public void testDeleteShardingKeyNonLeader() {
- MockWriter mockWriter = new MockWriter(DUMMY_NAMESPACE, ZK_ADDR);
+ MockWriter mockWriter = new MockWriter(TEST_NAMESPACE, _zkAddrTestNS);
mockWriter.deleteShardingKey(DUMMY_REALM, DUMMY_SHARDING_KEY);
- Assert.assertEquals("DELETE", mockWriter.calledRequest.getMethod());
+ Assert
+ .assertEquals(mockWriter.calledRequest.getMethod(), HttpConstants.RestVerbs.DELETE.name());
List<String> expectedUrlParams = Arrays
- .asList(MetadataStoreRoutingConstants.MSDS_NAMESPACES_URL_PREFIX, DUMMY_NAMESPACE,
+ .asList(MetadataStoreRoutingConstants.MSDS_NAMESPACES_URL_PREFIX, TEST_NAMESPACE,
MetadataStoreRoutingConstants.MSDS_GET_ALL_REALMS_ENDPOINT, DUMMY_REALM,
MetadataStoreRoutingConstants.MSDS_GET_ALL_SHARDING_KEYS_ENDPOINT, DUMMY_SHARDING_KEY);
String expectedUrl =
getBaseUri().toString() + String.join("/", expectedUrlParams).replaceAll("//", "/")
.substring(1);
- Assert.assertEquals(expectedUrl, mockWriter.calledRequest.getURI().toString());
+ Assert.assertEquals(mockWriter.calledRequest.getURI().toString(), expectedUrl);
mockWriter.close();
}
+
+ @Test(dependsOnMethods = "testDeleteShardingKeyNonLeader")
+ public void testSetRoutingDataNonLeader() {
+ MockWriter mockWriter = new MockWriter(TEST_NAMESPACE, _zkAddrTestNS);
+ Map<String, List<String>> testRoutingDataMap =
+ ImmutableMap.of(DUMMY_REALM, Collections.singletonList(DUMMY_SHARDING_KEY));
+ mockWriter.setRoutingData(testRoutingDataMap);
+ Assert.assertEquals(mockWriter.calledRequest.getMethod(), HttpConstants.RestVerbs.PUT.name());
+ List<String> expectedUrlParams = Arrays
+ .asList(MetadataStoreRoutingConstants.MSDS_NAMESPACES_URL_PREFIX, TEST_NAMESPACE,
+ MetadataStoreRoutingConstants.MSDS_GET_ALL_ROUTING_DATA_ENDPOINT);
+ String expectedUrl =
+ getBaseUri().toString() + String.join("/", expectedUrlParams).replaceAll("//", "/")
+ .substring(1);
+ Assert.assertEquals(mockWriter.calledRequest.getURI().toString(), expectedUrl);
+ mockWriter.close();
+ }
+
+ private void clearRoutingDataPath() throws Exception {
+ Assert.assertTrue(TestHelper.verify(() -> {
+ for (String zkRealm : _zkClient
+ .getChildren(MetadataStoreRoutingConstants.ROUTING_DATA_PATH)) {
+ _zkClient.delete(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + zkRealm);
+ }
+
+ return _zkClient.getChildren(MetadataStoreRoutingConstants.ROUTING_DATA_PATH).isEmpty();
+ }, TestHelper.WAIT_DURATION), "Routing data path should be deleted after the tests.");
+ }
}
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/MetadataStoreDirectoryAccessorTestBase.java b/helix-rest/src/test/java/org/apache/helix/rest/server/MetadataStoreDirectoryAccessorTestBase.java
index 7cebbf3..f2ed433 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/MetadataStoreDirectoryAccessorTestBase.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/MetadataStoreDirectoryAccessorTestBase.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.apache.helix.TestHelper;
@@ -32,6 +33,7 @@ import org.apache.helix.rest.metadatastore.accessor.MetadataStoreRoutingDataRead
import org.apache.helix.rest.metadatastore.accessor.ZkRoutingDataReader;
import org.apache.helix.zookeeper.datamodel.ZNRecord;
import org.apache.helix.zookeeper.datamodel.serializer.ZNRecordSerializer;
+import org.apache.helix.zookeeper.zkclient.ZkClient;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@@ -62,7 +64,7 @@ public class MetadataStoreDirectoryAccessorTestBase extends AbstractTestClass {
public void beforeClass() throws Exception {
_zkList = new ArrayList<>(ZK_SERVER_MAP.keySet());
- deleteRoutingDataPath();
+ clearRoutingData();
// Write dummy mappings in ZK
// Create a node that represents a realm address and add 3 sharding keys to it
@@ -100,21 +102,29 @@ public class MetadataStoreDirectoryAccessorTestBase extends AbstractTestClass {
@AfterClass
public void afterClass() throws Exception {
System.clearProperty(MetadataStoreRoutingConstants.MSDS_SERVER_HOSTNAME_KEY);
- deleteRoutingDataPath();
+ _routingDataReader.close();
+ clearRoutingData();
}
- protected void deleteRoutingDataPath() throws Exception {
+ protected void clearRoutingData() throws Exception {
Assert.assertTrue(TestHelper.verify(() -> {
- _zkList.forEach(zk -> ZK_SERVER_MAP.get(zk).getZkClient()
- .deleteRecursively(MetadataStoreRoutingConstants.ROUTING_DATA_PATH));
+ for (String zk : _zkList) {
+ ZkClient zkClient = ZK_SERVER_MAP.get(zk).getZkClient();
+ if (zkClient.exists(MetadataStoreRoutingConstants.ROUTING_DATA_PATH)) {
+ for (String zkRealm : zkClient
+ .getChildren(MetadataStoreRoutingConstants.ROUTING_DATA_PATH)) {
+ zkClient.delete(MetadataStoreRoutingConstants.ROUTING_DATA_PATH + "/" + zkRealm);
+ }
+ }
+ }
for (String zk : _zkList) {
- if (ZK_SERVER_MAP.get(zk).getZkClient()
- .exists(MetadataStoreRoutingConstants.ROUTING_DATA_PATH)) {
+ ZkClient zkClient = ZK_SERVER_MAP.get(zk).getZkClient();
+ if (zkClient.exists(MetadataStoreRoutingConstants.ROUTING_DATA_PATH) && !zkClient
+ .getChildren(MetadataStoreRoutingConstants.ROUTING_DATA_PATH).isEmpty()) {
return false;
}
}
-
return true;
}, TestHelper.WAIT_DURATION), "Routing data path should be deleted after the tests.");
}
@@ -129,4 +139,8 @@ public class MetadataStoreDirectoryAccessorTestBase extends AbstractTestClass {
protected Set<String> getAllShardingKeysInTestRealm1() throws InvalidRoutingDataException {
return new HashSet<>(_routingDataReader.getRoutingData().get(TEST_REALM_1));
}
+
+ protected Map<String, List<String>> getRawRoutingData() throws InvalidRoutingDataException {
+ return _routingDataReader.getRoutingData();
+ }
}
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/TestMSDAccessorLeaderElection.java b/helix-rest/src/test/java/org/apache/helix/rest/server/TestMSDAccessorLeaderElection.java
index 951015b..2a3f0be 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/TestMSDAccessorLeaderElection.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/TestMSDAccessorLeaderElection.java
@@ -24,7 +24,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javax.ws.rs.core.Response;
@@ -45,8 +47,11 @@ import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
+import org.codehaus.jackson.map.ObjectMapper;
import org.glassfish.jersey.server.ResourceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -120,8 +125,9 @@ public class TestMSDAccessorLeaderElection extends MetadataStoreDirectoryAccesso
Set<String> expectedRealmsSet = getAllRealms();
Assert.assertFalse(expectedRealmsSet.contains(TEST_REALM_3),
"Metadata store directory should not have realm: " + TEST_REALM_3);
- sendRequestAndValidate("/metadata-store-realms/" + TEST_REALM_3, HttpConstants.RestVerbs.PUT,
- Response.Status.CREATED.getStatusCode());
+ HttpUriRequest request =
+ buildRequest("/metadata-store-realms/" + TEST_REALM_3, HttpConstants.RestVerbs.PUT, "");
+ sendRequestAndValidate(request, Response.Status.CREATED.getStatusCode());
expectedRealmsSet.add(TEST_REALM_3);
Assert.assertEquals(getAllRealms(), expectedRealmsSet);
MockMetadataStoreDirectoryAccessor._mockMSDInstance.close();
@@ -131,8 +137,9 @@ public class TestMSDAccessorLeaderElection extends MetadataStoreDirectoryAccesso
public void testDeleteMetadataStoreRealmRequestForwarding()
throws InvalidRoutingDataException, IOException {
Set<String> expectedRealmsSet = getAllRealms();
- sendRequestAndValidate("/metadata-store-realms/" + TEST_REALM_3, HttpConstants.RestVerbs.DELETE,
- Response.Status.OK.getStatusCode());
+ HttpUriRequest request =
+ buildRequest("/metadata-store-realms/" + TEST_REALM_3, HttpConstants.RestVerbs.DELETE, "");
+ sendRequestAndValidate(request, Response.Status.OK.getStatusCode());
expectedRealmsSet.remove(TEST_REALM_3);
Assert.assertEquals(getAllRealms(), expectedRealmsSet);
MockMetadataStoreDirectoryAccessor._mockMSDInstance.close();
@@ -144,9 +151,10 @@ public class TestMSDAccessorLeaderElection extends MetadataStoreDirectoryAccesso
Set<String> expectedShardingKeysSet = getAllShardingKeysInTestRealm1();
Assert.assertFalse(expectedShardingKeysSet.contains(TEST_SHARDING_KEY),
"Realm does not have sharding key: " + TEST_SHARDING_KEY);
- sendRequestAndValidate(
+ HttpUriRequest request = buildRequest(
"/metadata-store-realms/" + TEST_REALM_1 + "/sharding-keys/" + TEST_SHARDING_KEY,
- HttpConstants.RestVerbs.PUT, Response.Status.CREATED.getStatusCode());
+ HttpConstants.RestVerbs.PUT, "");
+ sendRequestAndValidate(request, Response.Status.CREATED.getStatusCode());
expectedShardingKeysSet.add(TEST_SHARDING_KEY);
Assert.assertEquals(getAllShardingKeysInTestRealm1(), expectedShardingKeysSet);
MockMetadataStoreDirectoryAccessor._mockMSDInstance.close();
@@ -156,28 +164,47 @@ public class TestMSDAccessorLeaderElection extends MetadataStoreDirectoryAccesso
public void testDeleteShardingKeyRequestForwarding()
throws InvalidRoutingDataException, IOException {
Set<String> expectedShardingKeysSet = getAllShardingKeysInTestRealm1();
- sendRequestAndValidate(
+ HttpUriRequest request = buildRequest(
"/metadata-store-realms/" + TEST_REALM_1 + "/sharding-keys/" + TEST_SHARDING_KEY,
- HttpConstants.RestVerbs.DELETE, Response.Status.OK.getStatusCode());
+ HttpConstants.RestVerbs.DELETE, "");
+ sendRequestAndValidate(request, Response.Status.OK.getStatusCode());
expectedShardingKeysSet.remove(TEST_SHARDING_KEY);
Assert.assertEquals(getAllShardingKeysInTestRealm1(), expectedShardingKeysSet);
MockMetadataStoreDirectoryAccessor._mockMSDInstance.close();
}
- private void sendRequestAndValidate(String urlSuffix, HttpConstants.RestVerbs requestMethod,
- int expectedResponseCode) throws IllegalArgumentException, IOException {
+ @Test(dependsOnMethods = "testDeleteShardingKeyRequestForwarding")
+ public void testSetRoutingDataRequestForwarding()
+ throws InvalidRoutingDataException, IOException {
+ Map<String, List<String>> routingData = new HashMap<>();
+ routingData.put(TEST_REALM_1, TEST_SHARDING_KEYS_2);
+ routingData.put(TEST_REALM_2, TEST_SHARDING_KEYS_1);
+ String routingDataString = new ObjectMapper().writeValueAsString(routingData);
+ HttpUriRequest request =
+ buildRequest(MetadataStoreRoutingConstants.MSDS_GET_ALL_ROUTING_DATA_ENDPOINT,
+ HttpConstants.RestVerbs.PUT, routingDataString);
+ sendRequestAndValidate(request, Response.Status.CREATED.getStatusCode());
+ Assert.assertEquals(getRawRoutingData(), routingData);
+ MockMetadataStoreDirectoryAccessor._mockMSDInstance.close();
+ }
+
+ private HttpUriRequest buildRequest(String urlSuffix, HttpConstants.RestVerbs requestMethod,
+ String jsonEntity) {
String url = _mockBaseUri + TEST_NAMESPACE_URI_PREFIX + MOCK_URL_PREFIX + urlSuffix;
- HttpUriRequest request;
switch (requestMethod) {
case PUT:
- request = new HttpPut(url);
- break;
+ HttpPut httpPut = new HttpPut(url);
+ httpPut.setEntity(new StringEntity(jsonEntity, ContentType.APPLICATION_JSON));
+ return httpPut;
case DELETE:
- request = new HttpDelete(url);
- break;
+ return new HttpDelete(url);
default:
throw new IllegalArgumentException("Unsupported requestMethod: " + requestMethod);
}
+ }
+
+ private void sendRequestAndValidate(HttpUriRequest request, int expectedResponseCode)
+ throws IllegalArgumentException, IOException {
HttpResponse response = _httpClient.execute(request);
Assert.assertEquals(response.getStatusLine().getStatusCode(), expectedResponseCode);
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/TestMetadataStoreDirectoryAccessor.java b/helix-rest/src/test/java/org/apache/helix/rest/server/TestMetadataStoreDirectoryAccessor.java
index 94641ff..570ab90 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/TestMetadataStoreDirectoryAccessor.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/TestMetadataStoreDirectoryAccessor.java
@@ -21,6 +21,7 @@ package org.apache.helix.rest.server;
import java.io.IOException;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -452,6 +453,43 @@ public class TestMetadataStoreDirectoryAccessor extends MetadataStoreDirectoryAc
Assert.assertEquals(getAllShardingKeysInTestRealm1(), expectedShardingKeysSet);
}
+ @Test(dependsOnMethods = "testDeleteShardingKey")
+ public void testSetRoutingData() throws InvalidRoutingDataException, IOException {
+ Map<String, List<String>> routingData = new HashMap<>();
+ routingData.put(TEST_REALM_1, TEST_SHARDING_KEYS_2);
+ routingData.put(TEST_REALM_2, TEST_SHARDING_KEYS_1);
+ String routingDataString = OBJECT_MAPPER.writeValueAsString(routingData);
+
+ Map<String, String> badFormatRoutingData = new HashMap<>();
+ badFormatRoutingData.put(TEST_REALM_1, TEST_REALM_2);
+ badFormatRoutingData.put(TEST_REALM_2, TEST_REALM_1);
+ String badFormatRoutingDataString = OBJECT_MAPPER.writeValueAsString(badFormatRoutingData);
+
+ // Request that gets not found response.
+ put("/namespaces/non-existing-namespace"
+ + MetadataStoreRoutingConstants.MSDS_GET_ALL_ROUTING_DATA_ENDPOINT, null,
+ Entity.entity(routingDataString, MediaType.APPLICATION_JSON_TYPE),
+ Response.Status.NOT_FOUND.getStatusCode());
+
+ put(TEST_NAMESPACE_URI_PREFIX
+ + MetadataStoreRoutingConstants.MSDS_GET_ALL_ROUTING_DATA_ENDPOINT, null,
+ Entity.entity("?", MediaType.APPLICATION_JSON_TYPE),
+ Response.Status.BAD_REQUEST.getStatusCode());
+
+ put(TEST_NAMESPACE_URI_PREFIX
+ + MetadataStoreRoutingConstants.MSDS_GET_ALL_ROUTING_DATA_ENDPOINT, null,
+ Entity.entity(badFormatRoutingDataString, MediaType.APPLICATION_JSON_TYPE),
+ Response.Status.BAD_REQUEST.getStatusCode());
+
+ // Successful request.
+ put(TEST_NAMESPACE_URI_PREFIX
+ + MetadataStoreRoutingConstants.MSDS_GET_ALL_ROUTING_DATA_ENDPOINT, null,
+ Entity.entity(routingDataString, MediaType.APPLICATION_JSON_TYPE),
+ Response.Status.CREATED.getStatusCode());
+
+ Assert.assertEquals(getRawRoutingData(), routingData);
+ }
+
private void verifyRealmShardingKeys(String responseBody) throws IOException {
// It is safe to cast the object and suppress warnings.
@SuppressWarnings("unchecked")
diff --git a/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/datamodel/TrieRoutingData.java b/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/datamodel/TrieRoutingData.java
index 0f53c23..94bb331 100644
--- a/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/datamodel/TrieRoutingData.java
+++ b/metadata-store-directory-common/src/main/java/org/apache/helix/msdcommon/datamodel/TrieRoutingData.java
@@ -167,8 +167,8 @@ public class TrieRoutingData implements MetadataStoreRoutingData {
}
/*
- * Checks for the edge case when the only sharding key in provided routing data is the delimiter
- * or an empty string. When this is the case, the trie is valid and contains only one node, which
+ * Checks for the edge case when the only sharding key in provided routing data is the delimiter.
+ * When this is the case, the trie is valid and contains only one node, which
* is the root node, and the root node is a leaf node with a realm address associated with it.
* @param routingData - a mapping from "sharding keys" to "realm addresses" to be parsed into a
* trie