You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@curator.apache.org by ti...@apache.org on 2022/06/26 12:34:48 UTC

[curator] branch master updated: CURATOR-643: Add option to disable parent creation for PersistentTtlNode (#422)

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 <pa...@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
     {