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 99841338e00ea3eb0c572fd8f5e63e9d17edca1c
Author: Neal Sun <[email protected]>
AuthorDate: Tue Feb 4 17:53:16 2020 -0800

    Add MetadataStoreRoutingDataReader interface and ZkRoutingDataReader class 
to helix-rest (#714)
    
    This PR adds an interface and an implementation for metadata store routing 
data accessing. The interface works directly with MetadataStoreRoutingData, 
acting as an abstract layer that allows the data to be accessed from different 
sources. The implementation focuses on accessing data from ZooKeeper.
---
 .../MetadataStoreRoutingDataReader.java            |  46 ++++++++
 .../rest/metadatastore/ZkRoutingDataReader.java    |  75 ++++++++++++
 .../exceptions/InvalidRoutingDataException.java    |  30 +++++
 .../metadatastore/TestZkRoutingDataReader.java     | 131 +++++++++++++++++++++
 4 files changed, 282 insertions(+)

diff --git 
a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/MetadataStoreRoutingDataReader.java
 
b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/MetadataStoreRoutingDataReader.java
new file mode 100644
index 0000000..3cc9a06
--- /dev/null
+++ 
b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/MetadataStoreRoutingDataReader.java
@@ -0,0 +1,46 @@
+package org.apache.helix.rest.metadatastore;
+
+/*
+ * 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.List;
+import java.util.Map;
+import 
org.apache.helix.rest.metadatastore.exceptions.InvalidRoutingDataException;
+
+/**
+ * An interface for a DAO that fetches routing data from a source and return a 
key-value mapping
+ * that represent the said routing data.
+ */
+public interface MetadataStoreRoutingDataReader {
+
+  /**
+   * Fetches routing data from the data source.
+   * @return a mapping from "metadata store realm addresses" to lists of 
"metadata store sharding
+   *         keys", where the sharding keys in a value list all route to the 
realm address in the
+   *         key
+   * @throws InvalidRoutingDataException - when the routing data is malformed 
in any way that
+   *           disallows a meaningful mapping to be returned
+   */
+  Map<String, List<String>> getRoutingData() throws 
InvalidRoutingDataException;
+
+  /**
+   * Closes any stateful resources such as connections or threads.
+   */
+  void close();
+}
diff --git 
a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/ZkRoutingDataReader.java
 
b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/ZkRoutingDataReader.java
new file mode 100644
index 0000000..a4c7e1c
--- /dev/null
+++ 
b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/ZkRoutingDataReader.java
@@ -0,0 +1,75 @@
+package org.apache.helix.rest.metadatastore;
+
+/*
+ * 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.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.I0Itec.zkclient.exception.ZkNoNodeException;
+import org.apache.helix.ZNRecord;
+import org.apache.helix.manager.zk.ZNRecordSerializer;
+import org.apache.helix.manager.zk.client.DedicatedZkClientFactory;
+import org.apache.helix.manager.zk.client.HelixZkClient;
+import 
org.apache.helix.rest.metadatastore.exceptions.InvalidRoutingDataException;
+
+public class ZkRoutingDataReader implements MetadataStoreRoutingDataReader {
+  static final String ROUTING_DATA_PATH = "/METADATA_STORE_ROUTING_DATA";
+  static final String ZNRECORD_LIST_FIELD_KEY = "ZK_PATH_SHARDING_KEYS";
+
+  private final String _zkAddress;
+  private final HelixZkClient _zkClient;
+
+  public ZkRoutingDataReader(String zkAddress) {
+    _zkAddress = zkAddress;
+    _zkClient = DedicatedZkClientFactory.getInstance().buildZkClient(
+        new HelixZkClient.ZkConnectionConfig(zkAddress),
+        new HelixZkClient.ZkClientConfig().setZkSerializer(new 
ZNRecordSerializer()));
+  }
+
+  public Map<String, List<String>> getRoutingData() throws 
InvalidRoutingDataException {
+    Map<String, List<String>> routingData = new HashMap<>();
+    List<String> children;
+    try {
+      children = _zkClient.getChildren(ROUTING_DATA_PATH);
+    } catch (ZkNoNodeException e) {
+      throw new InvalidRoutingDataException("Routing data directory ZNode " + 
ROUTING_DATA_PATH
+          + " does not exist. Routing ZooKeeper address: " + _zkAddress);
+    }
+    if (children == null || children.isEmpty()) {
+      throw new InvalidRoutingDataException(
+          "There are no metadata store realms defined. Routing ZooKeeper 
address: " + _zkAddress);
+    }
+    for (String child : children) {
+      ZNRecord record = _zkClient.readData(ROUTING_DATA_PATH + "/" + child);
+      List<String> shardingKeys = record.getListField(ZNRECORD_LIST_FIELD_KEY);
+      if (shardingKeys == null || shardingKeys.isEmpty()) {
+        throw new InvalidRoutingDataException("Realm address ZNode " + 
ROUTING_DATA_PATH + "/"
+            + child + " does not have a value for key " + 
ZNRECORD_LIST_FIELD_KEY
+            + ". Routing ZooKeeper address: " + _zkAddress);
+      }
+      routingData.put(child, shardingKeys);
+    }
+    return routingData;
+  }
+
+  public void close() {
+    _zkClient.close();
+  }
+}
diff --git 
a/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/exceptions/InvalidRoutingDataException.java
 
b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/exceptions/InvalidRoutingDataException.java
new file mode 100644
index 0000000..267aadc
--- /dev/null
+++ 
b/helix-rest/src/main/java/org/apache/helix/rest/metadatastore/exceptions/InvalidRoutingDataException.java
@@ -0,0 +1,30 @@
+package org.apache.helix.rest.metadatastore.exceptions;
+
+/*
+ * 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.
+ */
+
+/**
+ * This exception is thrown by MetadataStoreRoutingDataAccessor when the 
routing data it's trying to
+ * access is malformed and is there invalid.
+ */
+public class InvalidRoutingDataException extends Exception {
+  public InvalidRoutingDataException(String info) {
+    super(info);
+  }
+}
diff --git 
a/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/TestZkRoutingDataReader.java
 
b/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/TestZkRoutingDataReader.java
new file mode 100644
index 0000000..d06c38d
--- /dev/null
+++ 
b/helix-rest/src/test/java/org/apache/helix/rest/metadatastore/TestZkRoutingDataReader.java
@@ -0,0 +1,131 @@
+package org.apache.helix.rest.metadatastore;
+
+/*
+ * 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.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.helix.AccessOption;
+import org.apache.helix.ZNRecord;
+import 
org.apache.helix.rest.metadatastore.exceptions.InvalidRoutingDataException;
+import org.apache.helix.rest.server.AbstractTestClass;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class TestZkRoutingDataReader extends AbstractTestClass {
+  private MetadataStoreRoutingDataReader _zkRoutingDataReader;
+
+  @BeforeClass
+  public void beforeClass() {
+    _zkRoutingDataReader = new ZkRoutingDataReader(ZK_ADDR);
+  }
+
+  @AfterClass
+  public void afterClass() {
+    _zkRoutingDataReader.close();
+  }
+
+  @AfterMethod
+  public void afterMethod() {
+    _baseAccessor.remove(ZkRoutingDataReader.ROUTING_DATA_PATH, 
AccessOption.PERSISTENT);
+  }
+
+  @Test
+  public void testGetRoutingData() {
+    // Create a node that represents a realm address and add 3 sharding keys 
to it
+    ZNRecord testZnRecord1 = new ZNRecord("testZnRecord1");
+    List<String> testShardingKeys1 =
+        Arrays.asList("/sharding/key/1/a", "/sharding/key/1/b", 
"/sharding/key/1/c");
+    testZnRecord1.setListField(ZkRoutingDataReader.ZNRECORD_LIST_FIELD_KEY, 
testShardingKeys1);
+
+    // Create another node that represents a realm address and add 3 sharding 
keys to it
+    ZNRecord testZnRecord2 = new ZNRecord("testZnRecord2");
+    List<String> testShardingKeys2 = Arrays.asList("/sharding/key/2/a", 
"/sharding/key/2/b",
+        "/sharding/key/2/c", "/sharding/key/2/d");
+    testZnRecord2.setListField(ZkRoutingDataReader.ZNRECORD_LIST_FIELD_KEY, 
testShardingKeys2);
+
+    // Add both nodes as children nodes to 
ZkRoutingDataReader.ROUTING_DATA_PATH
+    _baseAccessor.create(ZkRoutingDataReader.ROUTING_DATA_PATH + 
"/testRealmAddress1",
+        testZnRecord1, AccessOption.PERSISTENT);
+    _baseAccessor.create(ZkRoutingDataReader.ROUTING_DATA_PATH + 
"/testRealmAddress2",
+        testZnRecord2, AccessOption.PERSISTENT);
+
+    MetadataStoreRoutingDataReader zkRoutingDataReader = new 
ZkRoutingDataReader(ZK_ADDR);
+    try {
+      Map<String, List<String>> routingData = 
zkRoutingDataReader.getRoutingData();
+      Assert.assertEquals(routingData.size(), 2);
+      Assert.assertEquals(routingData.get("testRealmAddress1"), 
testShardingKeys1);
+      Assert.assertEquals(routingData.get("testRealmAddress2"), 
testShardingKeys2);
+    } catch (InvalidRoutingDataException e) {
+      Assert.fail("Not expecting InvalidRoutingDataException");
+    }
+  }
+
+  @Test
+  public void testGetRoutingDataMissingMSRD() {
+    MetadataStoreRoutingDataReader zkRoutingDataReader = new 
ZkRoutingDataReader(ZK_ADDR);
+    try {
+      zkRoutingDataReader.getRoutingData();
+      Assert.fail("Expecting InvalidRoutingDataException");
+    } catch (InvalidRoutingDataException e) {
+      Assert.assertTrue(e.getMessage()
+          .contains("Routing data directory ZNode " + 
ZkRoutingDataReader.ROUTING_DATA_PATH
+              + " does not exist. Routing ZooKeeper address: " + ZK_ADDR));
+    }
+  }
+
+  @Test
+  public void testGetRoutingDataMissingMSRDChildren() {
+    _baseAccessor.create(ZkRoutingDataReader.ROUTING_DATA_PATH, new 
ZNRecord("test"),
+        AccessOption.PERSISTENT);
+    MetadataStoreRoutingDataReader zkRoutingDataReader = new 
ZkRoutingDataReader(ZK_ADDR);
+    try {
+      zkRoutingDataReader.getRoutingData();
+      Assert.fail("Expecting InvalidRoutingDataException");
+    } catch (InvalidRoutingDataException e) {
+      Assert.assertTrue(e.getMessage().contains(
+          "There are no metadata store realms defined. Routing ZooKeeper 
address: " + ZK_ADDR));
+    }
+  }
+
+  @Test
+  public void testGetRoutingDataMSRDChildEmptyValue() {
+    ZNRecord testZnRecord1 = new ZNRecord("testZnRecord1");
+    testZnRecord1.setListField(ZkRoutingDataReader.ZNRECORD_LIST_FIELD_KEY,
+        Collections.emptyList());
+    _baseAccessor.create(ZkRoutingDataReader.ROUTING_DATA_PATH + 
"/testRealmAddress1",
+        testZnRecord1, AccessOption.PERSISTENT);
+    MetadataStoreRoutingDataReader zkRoutingDataReader = new 
ZkRoutingDataReader(ZK_ADDR);
+    try {
+      zkRoutingDataReader.getRoutingData();
+      Assert.fail("Expecting InvalidRoutingDataException");
+    } catch (InvalidRoutingDataException e) {
+      Assert.assertTrue(e.getMessage()
+          .contains("Realm address ZNode " + 
ZkRoutingDataReader.ROUTING_DATA_PATH
+              + "/testRealmAddress1 does not have a value for key "
+              + ZkRoutingDataReader.ZNRECORD_LIST_FIELD_KEY + ". Routing 
ZooKeeper address: "
+              + ZK_ADDR));
+    }
+  }
+}

Reply via email to