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/01 22:47:01 UTC

[helix] 06/49: Add MetadataStoreRoutingDataReader interface and ZkRoutingDataReader class to helix-rest (#714)

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 dda53d10f89b4b44b301a10773559c030dec204a
Author: Neal Sun <ne...@gmail.com>
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));
+    }
+  }
+}