This is an automated email from the ASF dual-hosted git repository. nealsun pushed a commit to branch zookeeper-api-ttlcontainer in repository https://gitbox.apache.org/repos/asf/helix.git
commit 3fbf8072c039f075900fb9c6bd09bb4d250bf977 Author: Ramin Bashizade <[email protected]> AuthorDate: Wed May 18 05:44:15 2022 -0700 Add TTL and Container modes to sync create API in ZkClient and ZkConnection (#2090) This PR adds methods that support creating persistent nodes with Container and TTL modes synchronously to ZkClient and ZkConnection classes. --- .../helix/zookeeper/zkclient/IZkConnection.java | 2 + .../apache/helix/zookeeper/zkclient/ZkClient.java | 327 ++++++++++++++++++++- .../helix/zookeeper/zkclient/ZkConnection.java | 6 + .../zookeeper/impl/client/TestRawZkClient.java | 129 ++++++++ 4 files changed, 455 insertions(+), 9 deletions(-) diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkConnection.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkConnection.java index 6fc040fd8..e766bf7d9 100644 --- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkConnection.java +++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/IZkConnection.java @@ -40,6 +40,8 @@ public interface IZkConnection { public String create(String path, byte[] data, List<ACL> acl, CreateMode mode) throws KeeperException, InterruptedException; + public String create(String path, byte[] data, List<ACL> acl, CreateMode mode, long ttl) throws KeeperException, InterruptedException; + public void delete(String path) throws InterruptedException, KeeperException; boolean exists(final String path, final boolean watch) throws KeeperException, InterruptedException; diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkClient.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkClient.java index c6b74239b..e4832656b 100644 --- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkClient.java +++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkClient.java @@ -438,6 +438,41 @@ public class ZkClient implements Watcher { createPersistent(path, false); } + /** + * Create a persistent node with TTL. + * @param path the path where you want the node to be created + * @param ttl TTL of the node in milliseconds + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public void createPersistentWithTTL(String path, long ttl) + throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { + createPersistentWithTTL(path, false, ttl); + } + + /** + * Create a container node. + * @param path the path where you want the node to be created + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public void createContainer(String path) + throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { + createContainer(path, false); + } + /** * Create a persistent node and set its ACLs. * @param path @@ -459,6 +494,45 @@ public class ZkClient implements Watcher { createPersistent(path, createParents, ZooDefs.Ids.OPEN_ACL_UNSAFE); } + /** + * Create a persistent node with TTL and set its ACLs. + * @param path the path where you want the node to be created + * @param createParents if true all parent dirs are created as well and no + * {@link ZkNodeExistsException} is thrown in case the path already exists + * @param ttl TTL of the node in milliseconds + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public void createPersistentWithTTL(String path, boolean createParents, long ttl) + throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { + createPersistentWithTTL(path, createParents, ZooDefs.Ids.OPEN_ACL_UNSAFE, ttl); + } + + /** + * Create a container node and set its ACLs. + * @param path the path where you want the node to be created + * @param createParents if true all parent dirs are created as well and no + * {@link ZkNodeExistsException} is thrown in case the path already exists + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public void createContainer(String path, boolean createParents) + throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { + createContainer(path, createParents, ZooDefs.Ids.OPEN_ACL_UNSAFE); + } + /** * Create a persistent node and set its ACLs. * @param path @@ -495,6 +569,73 @@ public class ZkClient implements Watcher { } } + /** + * Create a persistent node with TTL and set its ACLs. + * @param path the path where you want the node to be created + * @param createParents if true all parent dirs are created as well and no + * {@link ZkNodeExistsException} is thrown in case the path already exists + * @param acl List of ACL permissions to assign to the node + * @param ttl TTL of the node in milliseconds + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public void createPersistentWithTTL(String path, boolean createParents, List<ACL> acl, long ttl) + throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { + try { + create(path, null, acl, CreateMode.PERSISTENT_WITH_TTL, ttl); + } catch (ZkNodeExistsException e) { + if (!createParents) { + throw e; + } + } catch (ZkNoNodeException e) { + if (!createParents) { + throw e; + } + String parentDir = path.substring(0, path.lastIndexOf('/')); + createPersistentWithTTL(parentDir, createParents, acl, ttl); + createPersistentWithTTL(path, createParents, acl, ttl); + } + } + + /** + * Create a container node and set its ACLs. + * @param path the path where you want the node to be created + * @param createParents if true all parent dirs are created as well and no + * {@link ZkNodeExistsException} is thrown in case the path already exists + * @param acl List of ACL permissions to assign to the node + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public void createContainer(String path, boolean createParents, List<ACL> acl) + throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { + try { + create(path, null, acl, CreateMode.CONTAINER); + } catch (ZkNodeExistsException e) { + if (!createParents) { + throw e; + } + } catch (ZkNoNodeException e) { + if (!createParents) { + throw e; + } + String parentDir = path.substring(0, path.lastIndexOf('/')); + createContainer(parentDir, createParents, acl); + createContainer(path, createParents, acl); + } + } + /** * Create a persistent node. * @param path @@ -513,6 +654,43 @@ public class ZkClient implements Watcher { create(path, data, CreateMode.PERSISTENT); } + /** + * Create a persistent node with TTL. + * @param path the path where you want the node to be created + * @param data data of the node + * @param ttl TTL of the node in milliseconds + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public void createPersistentWithTTL(String path, Object data, long ttl) + throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { + create(path, data, CreateMode.PERSISTENT_WITH_TTL, ttl); + } + + /** + * Create a container node. + * @param path the path where you want the node to be created + * @param data data of the node + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public void createContainer(String path, Object data) + throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { + create(path, data, CreateMode.CONTAINER); + } + /** * Create a persistent node. * @param path @@ -531,6 +709,43 @@ public class ZkClient implements Watcher { create(path, data, acl, CreateMode.PERSISTENT); } + /** + * Create a persistent node with TTL. + * @param path the path where you want the node to be created + * @param data data of the node + * @param acl list of ACL for the node + * @param ttl TTL of the node in milliseconds + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public void createPersistentWithTTL(String path, Object data, List<ACL> acl, long ttl) { + create(path, data, acl, CreateMode.PERSISTENT_WITH_TTL, ttl); + } + + /** + * Create a container node. + * @param path the path where you want the node to be created + * @param data data of the node + * @param acl list of ACL for the node + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public void createContainer(String path, Object data, List<ACL> acl) { + create(path, data, acl, CreateMode.CONTAINER); + } + /** * Create a persistent, sequental node. * @param path @@ -550,6 +765,26 @@ public class ZkClient implements Watcher { return create(path, data, CreateMode.PERSISTENT_SEQUENTIAL); } + /** + * Create a persistent, sequential node. + * @param path the path where you want the node to be created + * @param data data of the node + * @param ttl TTL of the node in milliseconds + * @return create node's path + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public String createPersistentSequentialWithTTL(String path, Object data, long ttl) + throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { + return create(path, data, CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL, ttl); + } + /** * Create a persistent, sequential node and set its ACL. * @param path @@ -570,6 +805,27 @@ public class ZkClient implements Watcher { return create(path, data, acl, CreateMode.PERSISTENT_SEQUENTIAL); } + /** + * Create a persistent, sequential node and set its ACL. + * @param path the path where you want the node to be created + * @param acl list of ACL for the node + * @param data data of the node + * @param ttl TTL of the node in milliseconds + * @return create node's path + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public String createPersistentSequentialWithTTL(String path, Object data, List<ACL> acl, long ttl) + throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { + return create(path, data, acl, CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL, ttl); + } + /** * Create an ephemeral node. * @param path @@ -648,7 +904,7 @@ public class ZkClient implements Watcher { */ public void createEphemeral(final String path, final List<ACL> acl, final String sessionId) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { - create(path, null, acl, CreateMode.EPHEMERAL, sessionId); + create(path, null, acl, CreateMode.EPHEMERAL, TTL_NOT_SET, sessionId); } /** @@ -671,6 +927,28 @@ public class ZkClient implements Watcher { return create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, mode); } + /** + * Create a node. + * @param path the path where you want the node to be created + * @param data data of the node + * @param mode {@link CreateMode} of the node + * @param ttl TTL of the node in milliseconds, if mode is {@link CreateMode#PERSISTENT_WITH_TTL} + * or {@link CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL} + * @return create node's path + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public String create(final String path, Object data, final CreateMode mode, long ttl) + throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { + return create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, mode, ttl); + } + /** * Create a node with ACL. * @param path @@ -689,7 +967,30 @@ public class ZkClient implements Watcher { */ public String create(final String path, Object datat, final List<ACL> acl, final CreateMode mode) throws IllegalArgumentException, ZkException { - return create(path, datat, acl, mode, null); + return create(path, datat, acl, mode, TTL_NOT_SET, null); + } + + /** + * Create a node with ACL. + * @param path the path where you want the node to be created + * @param datat data of the node + * @param acl list of ACL for the node + * @param mode {@link CreateMode} of the node + * @param ttl TTL of the node in milliseconds, if mode is {@link CreateMode#PERSISTENT_WITH_TTL} + * or {@link CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL} + * @return create node's path + * @throws ZkInterruptedException + * if operation was interrupted, or a required reconnection got interrupted + * @throws IllegalArgumentException + * if called from anything except the ZooKeeper event thread + * @throws ZkException + * if any ZooKeeper exception occurred + * @throws RuntimeException + * if any other exception occurs + */ + public String create(final String path, Object datat, final List<ACL> acl, final CreateMode mode, + long ttl) throws IllegalArgumentException, ZkException { + return create(path, datat, acl, mode, ttl, null); } /** @@ -705,6 +1006,8 @@ public class ZkClient implements Watcher { * @param dataObject data of the node * @param acl list of ACL for the node * @param mode {@link CreateMode} of the node + * @param ttl TTL of the node in milliseconds, if mode is {@link CreateMode#PERSISTENT_WITH_TTL} + * or {@link CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL} * @param expectedSessionId the expected session ID of the ZK connection. It is not necessarily the * session ID of current ZK Connection. If the expected session ID is NOT null, * the node is guaranteed to be created in the expected session, or creation is @@ -715,7 +1018,7 @@ public class ZkClient implements Watcher { * @throws ZkException if any zookeeper exception occurs */ private String create(final String path, final Object dataObject, final List<ACL> acl, - final CreateMode mode, final String expectedSessionId) + final CreateMode mode, long ttl, final String expectedSessionId) throws IllegalArgumentException, ZkException { if (path == null) { throw new NullPointerException("Path must not be null."); @@ -728,8 +1031,14 @@ public class ZkClient implements Watcher { final byte[] dataBytes = dataObject == null ? null : serialize(dataObject, path); checkDataSizeLimit(path, dataBytes); - final String actualPath = retryUntilConnected( - () -> getExpectedZookeeper(expectedSessionId).create(path, dataBytes, acl, mode)); + final String actualPath; + if (mode.isTTL()) { + actualPath = retryUntilConnected(() -> getExpectedZookeeper(expectedSessionId) + .create(path, dataBytes, acl, mode, null, ttl)); + } else { + actualPath = retryUntilConnected(() -> getExpectedZookeeper(expectedSessionId) + .create(path, dataBytes, acl, mode)); + } record(path, dataBytes, startT, ZkClientMonitor.AccessType.WRITE); return actualPath; @@ -786,7 +1095,7 @@ public class ZkClient implements Watcher { */ public void createEphemeral(final String path, final Object data, final String sessionId) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { - create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, sessionId); + create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, TTL_NOT_SET, sessionId); } /** @@ -836,7 +1145,7 @@ public class ZkClient implements Watcher { public void createEphemeral(final String path, final Object data, final List<ACL> acl, final String sessionId) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { - create(path, data, acl, CreateMode.EPHEMERAL, sessionId); + create(path, data, acl, CreateMode.EPHEMERAL, TTL_NOT_SET, sessionId); } /** @@ -882,7 +1191,7 @@ public class ZkClient implements Watcher { public String createEphemeralSequential(final String path, final Object data, final List<ACL> acl, final String sessionId) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { - return create(path, data, acl, CreateMode.EPHEMERAL_SEQUENTIAL, sessionId); + return create(path, data, acl, CreateMode.EPHEMERAL_SEQUENTIAL, TTL_NOT_SET, sessionId); } /** @@ -914,7 +1223,7 @@ public class ZkClient implements Watcher { final String sessionId) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException { return create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, - sessionId); + TTL_NOT_SET, sessionId); } /** diff --git a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkConnection.java b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkConnection.java index 08a2fb9aa..01935919c 100644 --- a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkConnection.java +++ b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkConnection.java @@ -133,6 +133,12 @@ public class ZkConnection implements IZkConnection { return _zk.create(path, data, acl, mode); } + @Override + public String create(String path, byte[] data, List<ACL> acl, CreateMode mode, long ttl) + throws KeeperException, InterruptedException { + return _zk.create(path, data, acl, mode, null, ttl); + } + @Override public void delete(String path) throws InterruptedException, KeeperException { _zk.delete(path, -1); diff --git a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestRawZkClient.java b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestRawZkClient.java index 20d070b52..9e8a75a73 100644 --- a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestRawZkClient.java +++ b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/client/TestRawZkClient.java @@ -89,6 +89,135 @@ public class TestRawZkClient extends ZkTestBase { _zkClient.close(); } + @Test + void testUnimplementedTypes() { + // Make sure extended types are disabled + System.clearProperty("zookeeper.extendedTypesEnabled"); + + // Make sure the test path is clear + String parentPath = "/tmp"; + String path = "/tmp/unimplemented"; + _zkClient.deleteRecursively(parentPath); + + try { + long ttl = 1L; + _zkClient.createPersistentWithTTL(path, true, ttl); + } catch (ZkException e) { + AssertJUnit.assertTrue(e.getCause() instanceof KeeperException.UnimplementedException); + return; + } + + // Clean up + _zkClient.deleteRecursively(parentPath); + AssertJUnit.fail(); + } + + @Test + void testCreatePersistentWithTTL() { + // Enable extended types and create a ZkClient + System.setProperty("zookeeper.extendedTypesEnabled", "true"); + ZkClient zkClient = new ZkClient(ZkTestBase.ZK_ADDR); + zkClient.setZkSerializer(new ZNRecordSerializer()); + + // Make sure the test path is clear + String parentPath = "/tmp"; + String path = "/tmp/createTTL"; + zkClient.deleteRecursively(parentPath); + AssertJUnit.assertFalse(zkClient.exists(parentPath)); + AssertJUnit.assertFalse(zkClient.exists(path)); + + long ttl = 1L; + ZNRecord record = new ZNRecord("record"); + String key = "key"; + String value = "value"; + record.setSimpleField(key, value); + + // Create a ZNode with the above ZNRecord and read back its data + zkClient.createPersistentWithTTL(parentPath, record, ttl); + AssertJUnit.assertTrue(zkClient.exists(parentPath)); + ZNRecord retrievedRecord = zkClient.readData(parentPath); + AssertJUnit.assertEquals(value, retrievedRecord.getSimpleField(key)); + + // Clear the path and test with createParents = true + AssertJUnit.assertTrue(zkClient.delete(parentPath)); + zkClient.createPersistentWithTTL(path, true, ttl); + AssertJUnit.assertTrue(zkClient.exists(path)); + + // Clean up + zkClient.deleteRecursively(parentPath); + zkClient.close(); + System.clearProperty("zookeeper.extendedTypesEnabled"); + } + + @Test + void testCreatePersistentSequentialWithTTL() { + // Enable extended types and create a ZkClient + System.setProperty("zookeeper.extendedTypesEnabled", "true"); + ZkClient zkClient = new ZkClient(ZkTestBase.ZK_ADDR); + zkClient.setZkSerializer(new ZNRecordSerializer()); + + // Make sure the test path is clear + String parentPath = "/tmp"; + String path = "/tmp/createSequentialTTL"; + zkClient.deleteRecursively(parentPath); + AssertJUnit.assertFalse(zkClient.exists(parentPath)); + AssertJUnit.assertFalse(zkClient.exists(path + "0000000000")); + + long ttl = 1L; + ZNRecord record = new ZNRecord("record"); + String key = "key"; + String value = "value"; + record.setSimpleField(key, value); + + // Create a ZNode with the above ZNRecord and read back its data + zkClient.createPersistent(parentPath); + zkClient.createPersistentSequentialWithTTL(path, record, ttl); + AssertJUnit.assertTrue(zkClient.exists(path + "0000000000")); + ZNRecord retrievedRecord = zkClient.readData(path + "0000000000"); + AssertJUnit.assertEquals(value, retrievedRecord.getSimpleField(key)); + + // Clean up + zkClient.deleteRecursively(parentPath); + zkClient.close(); + System.clearProperty("zookeeper.extendedTypesEnabled"); + } + + @Test + void testCreateContainer() { + // Enable extended types and create a ZkClient + System.setProperty("zookeeper.extendedTypesEnabled", "true"); + ZkClient zkClient = new ZkClient(ZkTestBase.ZK_ADDR); + zkClient.setZkSerializer(new ZNRecordSerializer()); + + // Make sure the test path is clear + String parentPath = "/tmp"; + String path = "/tmp/createContainer"; + zkClient.deleteRecursively(parentPath); + AssertJUnit.assertFalse(zkClient.exists(parentPath)); + AssertJUnit.assertFalse(zkClient.exists(path)); + + ZNRecord record = new ZNRecord("record"); + String key = "key"; + String value = "value"; + record.setSimpleField(key, value); + + // Create a ZNode with the above ZNRecord and read back its data + zkClient.createContainer(parentPath, record); + AssertJUnit.assertTrue(zkClient.exists(parentPath)); + ZNRecord retrievedRecord = zkClient.readData(parentPath); + AssertJUnit.assertEquals(value, retrievedRecord.getSimpleField(key)); + + // Clear the path and test with createParents = true + AssertJUnit.assertTrue(zkClient.delete(parentPath)); + zkClient.createContainer(path, true); + AssertJUnit.assertTrue(zkClient.exists(path)); + + // Clean up + zkClient.deleteRecursively(parentPath); + zkClient.close(); + System.clearProperty("zookeeper.extendedTypesEnabled"); + } + @Test() void testGetStat() { String path = "/tmp/getStatTest";
