You are viewing a plain text version of this content. The canonical link for it is here.
Posted to reviews@helix.apache.org by "qqu0127 (via GitHub)" <gi...@apache.org> on 2023/06/03 10:11:02 UTC

[GitHub] [helix] qqu0127 commented on a diff in pull request #2518: [helix/metaclient] ZkMetaClient Stress Tests (Part 1)

qqu0127 commented on code in PR #2518:
URL: https://github.com/apache/helix/pull/2518#discussion_r1215392234


##########
meta-client/src/test/java/org/apache/helix/metaclient/impl/zk/TestStressZkClient.java:
##########
@@ -1,24 +1,394 @@
 package org.apache.helix.metaclient.impl.zk;
 
 /*
-    * 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.
-    */
+ * 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.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.helix.metaclient.api.DataChangeListener;
+import org.apache.helix.metaclient.api.DataUpdater;
+import org.apache.helix.metaclient.api.MetaClientInterface;
+import org.apache.helix.metaclient.exception.MetaClientException;
+import org.apache.helix.metaclient.impl.zk.factory.ZkMetaClientConfig;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.testng.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+import static org.apache.helix.metaclient.api.MetaClientInterface.EntryMode.*;
 
 public class TestStressZkClient extends ZkMetaClientTestBase {
-  // TODO: add tests
+
+  private static final long TEST_ITERATION_COUNT = 1000;
+  private static final Logger LOG = LoggerFactory.getLogger(TestStressZkClient.class);
+
+  private void cleanUpTestNodes(String parentZnodeKey) {
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();

Review Comment:
   A new metaclient object is instantiated and connected, is this necessary?



##########
meta-client/src/test/java/org/apache/helix/metaclient/impl/zk/TestStressZkClient.java:
##########
@@ -1,24 +1,394 @@
 package org.apache.helix.metaclient.impl.zk;
 
 /*
-    * 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.
-    */
+ * 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.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.helix.metaclient.api.DataChangeListener;
+import org.apache.helix.metaclient.api.DataUpdater;
+import org.apache.helix.metaclient.api.MetaClientInterface;
+import org.apache.helix.metaclient.exception.MetaClientException;
+import org.apache.helix.metaclient.impl.zk.factory.ZkMetaClientConfig;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.testng.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+import static org.apache.helix.metaclient.api.MetaClientInterface.EntryMode.*;
 
 public class TestStressZkClient extends ZkMetaClientTestBase {
-  // TODO: add tests
+
+  private static final long TEST_ITERATION_COUNT = 1000;
+  private static final Logger LOG = LoggerFactory.getLogger(TestStressZkClient.class);
+
+  private void cleanUpTestNodes(String parentZnodeKey) {
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      int retryCount = 0;
+      while (zkMetaClient.countDirectChildren(parentZnodeKey) > 0) {
+        try {
+          retryCount++;
+          zkMetaClient.recursiveDelete(parentZnodeKey);
+        } catch (ZkClientException e) {
+          if (retryCount >= 3) {
+            throw new ZkClientException("Failed to clean up test nodes after 3 tries", e);
+          }
+        }
+      }
+      Assert.assertEquals(zkMetaClient.countDirectChildren(parentZnodeKey), 0);
+    }
+  }
+
+  @Test
+  public void testCreate() {
+    String zkParentKey = "/stressZk_testCreate";
+
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.create(zkParentKey + "/" + i, i);
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        Assert.assertEquals(String.valueOf(zkMetaClient.get(zkParentKey + "/" + i)), String.valueOf(i));
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.create("/a/b/c", "invalid_path");
+          Assert.fail("Should have failed with incorrect path.");
+        } catch (MetaClientException ignoredException) {
+        }
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.create("a/b/c", "invalid_path");
+          Assert.fail("Should have failed with invalid path - no leading /.");
+        } catch (Exception ignoredException) {
+        }
+      }
+      cleanUpTestNodes(zkParentKey);
+    }
+  }
+
+  @Test
+  public void testCreateContainer() {
+    final String zkParentKey = "/stressZk_testCreateContainer";
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.create(zkParentKey + "/" + i, i, CONTAINER);
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        Assert.assertEquals(String.valueOf(zkMetaClient.get(zkParentKey + "/" + i)), String.valueOf(i));
+      }
+
+      cleanUpTestNodes(zkParentKey);
+    }
+  }
+
+  @Test
+  public void testGet() {
+    final String zkParentKey = "/stressZk_testGet";
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.create(zkParentKey + "/" + i, i);
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        Assert.assertEquals(zkMetaClient.get(zkParentKey + "/" + i), i);
+      }
+
+      // Test with non-existent node path
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        Assert.assertNull(zkMetaClient.get("/a/b/c"));
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.get("a/b/c");
+          Assert.fail("Should have failed due to invalid path - no leading /.");
+        } catch (Exception ignoredException) {
+        }
+      }
+    }
+    cleanUpTestNodes(zkParentKey);
+  }
+
+  @Test
+  public void testSetSingleNode() {
+    final String zkParentKey = "/stressZk_testSetSingleNode";
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with no expected version
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.set(zkParentKey, String.valueOf(i), -1);
+        MetaClientInterface.Stat entryStat = zkMetaClient.exists(zkParentKey);
+
+        Assert.assertEquals(zkMetaClient.get(zkParentKey), String.valueOf(i));
+        Assert.assertEquals(entryStat.getVersion(), i + 1);
+        Assert.assertEquals(entryStat.getEntryType().name(), PERSISTENT.name());
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with expected version
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.set(zkParentKey, String.valueOf(i), i);
+        MetaClientInterface.Stat entryStat = zkMetaClient.exists(zkParentKey);
+
+        Assert.assertEquals(zkMetaClient.get(zkParentKey), String.valueOf(i));
+        Assert.assertEquals(entryStat.getVersion(), i + 1);
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with bad expected version - should fail
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.set(zkParentKey, "test-should-fail", 12345);
+          Assert.fail("Should have failed due to bad expected version in set function.");
+        } catch (MetaClientException ex) {
+          Assert.assertEquals(ex.getClass().getName(),
+              "org.apache.helix.metaclient.exception.MetaClientBadVersionException");
+        }
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with path to non-existent node - should fail
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.set("/a/b/c", "test-should-fail", -1);
+          Assert.fail("Should have failed due to path to non-existent node");
+        } catch (Exception ignoredException) {
+        }
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with invalid path - should fail
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.set("a/b/c", "test-should-fail", -1);
+          Assert.fail("Should have failed due to invalid path - no leading /.");
+        } catch (Exception ignoredException) {
+        }
+      }
+    }
+
+    cleanUpTestNodes(zkParentKey);
+  }
+
+  @Test
+  public void testSetMultiNode() {
+    final String zkParentKey = "/stressZk_testSetMultiNode";
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.create(zkParentKey + "/" + i, i);
+      }
+
+      // Set with no expected version
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.set(zkParentKey + "/" + i, "-" + i, -1);
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        String childKey = zkParentKey + "/" + i;
+        MetaClientInterface.Stat entryStat = zkMetaClient.exists(childKey);
+
+        Assert.assertEquals(zkMetaClient.get(childKey), "-" + i);
+        Assert.assertEquals(entryStat.getVersion(), 1);
+        Assert.assertEquals(entryStat.getEntryType().name(), PERSISTENT.name());
+      }
+
+      // Set with expected version
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        String childKey = zkParentKey + "/" + i;
+        zkMetaClient.set(childKey, String.valueOf(i), 1);
+        MetaClientInterface.Stat entryStat = zkMetaClient.exists(childKey);
+
+        Assert.assertEquals(zkMetaClient.get(childKey), String.valueOf(i));
+        Assert.assertEquals(entryStat.getVersion(), 2);
+      }
+    }
+    cleanUpTestNodes(zkParentKey);
+  }
+
+  @Test
+  public void testUpdateSingleNode() {
+    final String zkParentKey = "/stressZk_testUpdateSingleNode";
+    ZkMetaClientConfig config =
+        new ZkMetaClientConfig.ZkMetaClientConfigBuilder().setConnectionAddress(ZK_ADDR).build();
+    try (ZkMetaClient<Integer> zkMetaClient = new ZkMetaClient<>(config)) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, 0);
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        MetaClientInterface.Stat entryStat = zkMetaClient.exists(zkParentKey);
+        Assert.assertEquals(entryStat.getVersion(), i);
+
+        Integer newData = zkMetaClient.update(zkParentKey, new DataUpdater<Integer>() {
+          @Override
+          public Integer update(Integer currentData) {
+            return currentData + 1;
+          }
+        });
+        Assert.assertEquals((int) newData, i + 1);
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with path to non-existent node - should fail
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.update("/a/b/c", new DataUpdater<Integer>() {
+            @Override
+            public Integer update(Integer currentData) {
+              return currentData + 1;
+            }
+          });
+
+          Assert.fail("Should have failed due to path to non-existent node");
+        } catch (Exception ignoredException) {
+        }
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with invalid path - should fail
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.update("/a/b/c", new DataUpdater<Integer>() {
+            @Override
+            public Integer update(Integer currentData) {
+              return currentData + 1;
+            }
+          });
+          Assert.fail("Should have failed due to invalid path - no leading /.");
+        } catch (Exception ignoredException) {
+        }
+      }
+    }
+    cleanUpTestNodes(zkParentKey);
+  }
+
+  @Test
+  public void testUpdateMultiNode() {
+    final String zkParentKey = "/stressZk_testUpdateSingleNode";

Review Comment:
   nit: singleNode or multiNode?



##########
meta-client/src/test/java/org/apache/helix/metaclient/impl/zk/TestStressZkClient.java:
##########
@@ -1,24 +1,394 @@
 package org.apache.helix.metaclient.impl.zk;
 
 /*
-    * 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.
-    */
+ * 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.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.helix.metaclient.api.DataChangeListener;
+import org.apache.helix.metaclient.api.DataUpdater;
+import org.apache.helix.metaclient.api.MetaClientInterface;
+import org.apache.helix.metaclient.exception.MetaClientException;
+import org.apache.helix.metaclient.impl.zk.factory.ZkMetaClientConfig;
+import org.apache.helix.zookeeper.exception.ZkClientException;
+import org.testng.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+import static org.apache.helix.metaclient.api.MetaClientInterface.EntryMode.*;
 
 public class TestStressZkClient extends ZkMetaClientTestBase {
-  // TODO: add tests
+
+  private static final long TEST_ITERATION_COUNT = 1000;
+  private static final Logger LOG = LoggerFactory.getLogger(TestStressZkClient.class);
+
+  private void cleanUpTestNodes(String parentZnodeKey) {
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      int retryCount = 0;
+      while (zkMetaClient.countDirectChildren(parentZnodeKey) > 0) {
+        try {
+          retryCount++;
+          zkMetaClient.recursiveDelete(parentZnodeKey);
+        } catch (ZkClientException e) {
+          if (retryCount >= 3) {
+            throw new ZkClientException("Failed to clean up test nodes after 3 tries", e);
+          }
+        }
+      }
+      Assert.assertEquals(zkMetaClient.countDirectChildren(parentZnodeKey), 0);
+    }
+  }
+
+  @Test
+  public void testCreate() {
+    String zkParentKey = "/stressZk_testCreate";
+
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.create(zkParentKey + "/" + i, i);
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        Assert.assertEquals(String.valueOf(zkMetaClient.get(zkParentKey + "/" + i)), String.valueOf(i));
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.create("/a/b/c", "invalid_path");
+          Assert.fail("Should have failed with incorrect path.");
+        } catch (MetaClientException ignoredException) {
+        }
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.create("a/b/c", "invalid_path");
+          Assert.fail("Should have failed with invalid path - no leading /.");
+        } catch (Exception ignoredException) {
+        }
+      }
+      cleanUpTestNodes(zkParentKey);
+    }
+  }
+
+  @Test
+  public void testCreateContainer() {
+    final String zkParentKey = "/stressZk_testCreateContainer";
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.create(zkParentKey + "/" + i, i, CONTAINER);
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        Assert.assertEquals(String.valueOf(zkMetaClient.get(zkParentKey + "/" + i)), String.valueOf(i));
+      }
+
+      cleanUpTestNodes(zkParentKey);
+    }
+  }
+
+  @Test
+  public void testGet() {
+    final String zkParentKey = "/stressZk_testGet";
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.create(zkParentKey + "/" + i, i);
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        Assert.assertEquals(zkMetaClient.get(zkParentKey + "/" + i), i);
+      }
+
+      // Test with non-existent node path
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        Assert.assertNull(zkMetaClient.get("/a/b/c"));
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.get("a/b/c");
+          Assert.fail("Should have failed due to invalid path - no leading /.");
+        } catch (Exception ignoredException) {
+        }
+      }
+    }
+    cleanUpTestNodes(zkParentKey);
+  }
+
+  @Test
+  public void testSetSingleNode() {
+    final String zkParentKey = "/stressZk_testSetSingleNode";
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with no expected version
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.set(zkParentKey, String.valueOf(i), -1);
+        MetaClientInterface.Stat entryStat = zkMetaClient.exists(zkParentKey);
+
+        Assert.assertEquals(zkMetaClient.get(zkParentKey), String.valueOf(i));
+        Assert.assertEquals(entryStat.getVersion(), i + 1);
+        Assert.assertEquals(entryStat.getEntryType().name(), PERSISTENT.name());
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with expected version
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.set(zkParentKey, String.valueOf(i), i);
+        MetaClientInterface.Stat entryStat = zkMetaClient.exists(zkParentKey);
+
+        Assert.assertEquals(zkMetaClient.get(zkParentKey), String.valueOf(i));
+        Assert.assertEquals(entryStat.getVersion(), i + 1);
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with bad expected version - should fail
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.set(zkParentKey, "test-should-fail", 12345);
+          Assert.fail("Should have failed due to bad expected version in set function.");
+        } catch (MetaClientException ex) {
+          Assert.assertEquals(ex.getClass().getName(),
+              "org.apache.helix.metaclient.exception.MetaClientBadVersionException");
+        }
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with path to non-existent node - should fail
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.set("/a/b/c", "test-should-fail", -1);
+          Assert.fail("Should have failed due to path to non-existent node");
+        } catch (Exception ignoredException) {
+        }
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with invalid path - should fail
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.set("a/b/c", "test-should-fail", -1);
+          Assert.fail("Should have failed due to invalid path - no leading /.");
+        } catch (Exception ignoredException) {
+        }
+      }
+    }
+
+    cleanUpTestNodes(zkParentKey);
+  }
+
+  @Test
+  public void testSetMultiNode() {
+    final String zkParentKey = "/stressZk_testSetMultiNode";
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.create(zkParentKey + "/" + i, i);
+      }
+
+      // Set with no expected version
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.set(zkParentKey + "/" + i, "-" + i, -1);
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        String childKey = zkParentKey + "/" + i;
+        MetaClientInterface.Stat entryStat = zkMetaClient.exists(childKey);
+
+        Assert.assertEquals(zkMetaClient.get(childKey), "-" + i);
+        Assert.assertEquals(entryStat.getVersion(), 1);
+        Assert.assertEquals(entryStat.getEntryType().name(), PERSISTENT.name());
+      }
+
+      // Set with expected version
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        String childKey = zkParentKey + "/" + i;
+        zkMetaClient.set(childKey, String.valueOf(i), 1);
+        MetaClientInterface.Stat entryStat = zkMetaClient.exists(childKey);
+
+        Assert.assertEquals(zkMetaClient.get(childKey), String.valueOf(i));
+        Assert.assertEquals(entryStat.getVersion(), 2);
+      }
+    }
+    cleanUpTestNodes(zkParentKey);
+  }
+
+  @Test
+  public void testUpdateSingleNode() {
+    final String zkParentKey = "/stressZk_testUpdateSingleNode";
+    ZkMetaClientConfig config =
+        new ZkMetaClientConfig.ZkMetaClientConfigBuilder().setConnectionAddress(ZK_ADDR).build();
+    try (ZkMetaClient<Integer> zkMetaClient = new ZkMetaClient<>(config)) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, 0);
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        MetaClientInterface.Stat entryStat = zkMetaClient.exists(zkParentKey);
+        Assert.assertEquals(entryStat.getVersion(), i);
+
+        Integer newData = zkMetaClient.update(zkParentKey, new DataUpdater<Integer>() {
+          @Override
+          public Integer update(Integer currentData) {
+            return currentData + 1;
+          }
+        });
+        Assert.assertEquals((int) newData, i + 1);
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with path to non-existent node - should fail
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.update("/a/b/c", new DataUpdater<Integer>() {
+            @Override
+            public Integer update(Integer currentData) {
+              return currentData + 1;
+            }
+          });
+
+          Assert.fail("Should have failed due to path to non-existent node");
+        } catch (Exception ignoredException) {
+        }
+      }
+
+      zkMetaClient.delete(zkParentKey);
+      zkMetaClient.create(zkParentKey, "parent_node");
+
+      // Set with invalid path - should fail
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        try {
+          zkMetaClient.update("/a/b/c", new DataUpdater<Integer>() {
+            @Override
+            public Integer update(Integer currentData) {
+              return currentData + 1;
+            }
+          });
+          Assert.fail("Should have failed due to invalid path - no leading /.");
+        } catch (Exception ignoredException) {
+        }
+      }
+    }
+    cleanUpTestNodes(zkParentKey);
+  }
+
+  @Test
+  public void testUpdateMultiNode() {
+    final String zkParentKey = "/stressZk_testUpdateSingleNode";
+    ZkMetaClientConfig config =
+        new ZkMetaClientConfig.ZkMetaClientConfigBuilder().setConnectionAddress(ZK_ADDR).build();
+    try (ZkMetaClient<Integer> zkMetaClient = new ZkMetaClient<>(config)) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "test_parent");
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        zkMetaClient.create(zkParentKey + "/" + i, 0);
+      }
+
+      for (int i = 0; i < TEST_ITERATION_COUNT; i++) {
+        String childKey = zkParentKey + "/" + i;
+        MetaClientInterface.Stat entryStat = zkMetaClient.exists(childKey);
+        Assert.assertEquals(entryStat.getVersion(), 0);
+
+        Integer newData = zkMetaClient.update(childKey, new DataUpdater<Integer>() {
+          @Override
+          public Integer update(Integer currentData) {
+            return currentData + 1;
+          }
+        });
+
+        Assert.assertEquals((int) newData, 1);
+        entryStat = zkMetaClient.exists(childKey);
+        Assert.assertEquals(entryStat.getVersion(), 1);
+      }
+    }
+    cleanUpTestNodes(zkParentKey);
+  }
+
+  @Test
+  public void testMultipleDataChangeListeners() throws Exception {
+    final String zkParentKey = "/stressZk_testMultipleDataChangeListeners";
+    final int listenerCount = 10;
+    final String testData = "test-data";
+    final AtomicBoolean dataExpected = new AtomicBoolean(true);
+
+    try (ZkMetaClient<String> zkMetaClient = createZkMetaClient()) {
+      zkMetaClient.connect();
+      zkMetaClient.create(zkParentKey, "parent_node");
+      Map<String, Set<DataChangeListener>> listeners = new HashMap<>();

Review Comment:
   Do we need to store this?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: reviews-unsubscribe@helix.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: reviews-unsubscribe@helix.apache.org
For additional commands, e-mail: reviews-help@helix.apache.org