This is an automated email from the ASF dual-hosted git repository. tison pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/curator.git
The following commit(s) were added to refs/heads/master by this push: new f4abf511 CURATOR-643: Add option to disable parent creation for PersistentTtlNode (#422) f4abf511 is described below commit f4abf511c6fc514aae41ed75deb2483bf9726f24 Author: Paul Boutes <paul.bou...@gmail.com> AuthorDate: Sun Jun 26 14:34:41 2022 +0200 CURATOR-643: Add option to disable parent creation for PersistentTtlNode (#422) The PR enables the `PersistentTtlNode` to configure `useParentCreation` boolean flag, which is by default set as `true`. --- .../framework/recipes/nodes/PersistentTtlNode.java | 19 ++++++++-- .../recipes/nodes/TestPersistentTtlNode.java | 41 ++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentTtlNode.java b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentTtlNode.java index 91f5f714..9f019d6e 100644 --- a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentTtlNode.java +++ b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentTtlNode.java @@ -76,7 +76,19 @@ public class PersistentTtlNode implements Closeable */ public PersistentTtlNode(CuratorFramework client, String path, long ttlMs, byte[] initData) { - this(client, Executors.newSingleThreadScheduledExecutor(ThreadUtils.newThreadFactory("PersistentTtlNode")), path, ttlMs, initData, DEFAULT_CHILD_NODE_NAME, DEFAULT_TOUCH_SCHEDULE_FACTOR); + this(client, Executors.newSingleThreadScheduledExecutor(ThreadUtils.newThreadFactory("PersistentTtlNode")), path, ttlMs, initData, DEFAULT_CHILD_NODE_NAME, DEFAULT_TOUCH_SCHEDULE_FACTOR, true); + } + + /** + * @param client the client + * @param path path for the parent ZNode + * @param ttlMs max ttl for the node in milliseconds + * @param initData data for the node + * @param useParentCreation if true, parent ZNode can be created without ancestors + */ + public PersistentTtlNode(CuratorFramework client, String path, long ttlMs, byte[] initData, boolean useParentCreation) + { + this(client, Executors.newSingleThreadScheduledExecutor(ThreadUtils.newThreadFactory("PersistentTtlNode")), path, ttlMs, initData, DEFAULT_CHILD_NODE_NAME, DEFAULT_TOUCH_SCHEDULE_FACTOR, useParentCreation); } /** @@ -88,13 +100,14 @@ public class PersistentTtlNode implements Closeable * @param childNodeName name to use for the child node of the node created at <code>path</code> * @param touchScheduleFactor how ofter to set/create the child node as a factor of the ttlMs. i.e. * the child is touched every <code>(ttlMs / touchScheduleFactor)</code> + * @param useParentCreation if true, parent ZNode can be created without ancestors */ - public PersistentTtlNode(CuratorFramework client, ScheduledExecutorService executorService, String path, long ttlMs, byte[] initData, String childNodeName, int touchScheduleFactor) + public PersistentTtlNode(CuratorFramework client, ScheduledExecutorService executorService, String path, long ttlMs, byte[] initData, String childNodeName, int touchScheduleFactor, boolean useParentCreation) { this.client = Objects.requireNonNull(client, "client cannot be null"); this.ttlMs = ttlMs; this.touchScheduleFactor = touchScheduleFactor; - node = new PersistentNode(client, CreateMode.CONTAINER, false, path, initData) + node = new PersistentNode(client, CreateMode.CONTAINER, false, path, initData, useParentCreation) { @Override protected void deleteNode() diff --git a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentTtlNode.java b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentTtlNode.java index 040bb055..96d51383 100644 --- a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentTtlNode.java +++ b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentTtlNode.java @@ -22,6 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; @@ -31,6 +33,7 @@ import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.Timing; import org.apache.curator.test.compatibility.CuratorTestBase; +import org.apache.curator.test.compatibility.Timing2; import org.apache.curator.utils.ZKPaths; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -117,6 +120,44 @@ public class TestPersistentTtlNode extends CuratorTestBase } } + @Test + public void testRecreationOfParentNodeWithParentCreationOff() throws Exception + { + final byte[] TEST_DATA = "hey".getBytes(); + Timing2 timing = new Timing2(); + String containerPath = ZKPaths.makePath("test", "one", "two"); + String childPath = ZKPaths.makePath("test", "one", "two", PersistentTtlNode.DEFAULT_CHILD_NODE_NAME); + + try (CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1))) + { + client.start(); + client.create().creatingParentsIfNeeded().forPath("/test/one"); + + try (PersistentTtlNode node = new PersistentTtlNode(client, containerPath, ttlMs, TEST_DATA, false)) + { + node.start(); + assertTrue(node.waitForInitialCreate(timing.milliseconds(), TimeUnit.MILLISECONDS)); + + Thread.sleep(ttlMs + (ttlMs / 2)); + assertNotNull(client.checkExists().forPath(containerPath)); + assertNotNull(client.checkExists().forPath(childPath)); + + + client.delete().deletingChildrenIfNeeded().forPath("/test/one"); + timing.sleepABit(); + + // The underlying persistent node should not be able to recreate itself as the lazy parent creation is disabled + assertNull(client.checkExists().forPath(containerPath)); + assertNull(client.checkExists().forPath(childPath)); + + assertThrows(IllegalStateException.class, () -> node.setData(new byte[0])); + + // The underlying persistent node data should still be the initial one + assertArrayEquals(TEST_DATA, node.getData()); + } + } + } + @Test public void testEventsOnParent() throws Exception {