This is an automated email from the ASF dual-hosted git repository.
eolivelli 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 4a11aae CURATOR-590: Add option to disable parent creation for
PersistentNode
4a11aae is described below
commit 4a11aaef8b190dc220d35b7a91df294bfa06250e
Author: Paul Boutes <[email protected]>
AuthorDate: Tue Mar 9 08:11:49 2021 +0100
CURATOR-590: Add option to disable parent creation for PersistentNode
Adds a `useParentCreation` boolean flag to control the parent creation.
If this flag is set to `false`, the `PersistentNode` won't create the
underlying znodes with the `createParentContainersIfNeeded()`, meaning that the
parent znodes will have to exist beforehand in order for the `PersistentNode`
to succeed its creation.
The `useParentCreation` flag is set to `true` by default.
https://issues.apache.org/jira/browse/CURATOR-590
Author: Paul Boutes <[email protected]>
Author: Paul Boutes <[email protected]>
Reviewers: Enrico Olivelli <[email protected]>, Cameron McKenzie
<[email protected]>, Zili Chen <[email protected]>, Jordan Zimmerman
<[email protected]>
Closes #380 from pboutes/CURATOR-590
---
.../framework/recipes/nodes/PersistentNode.java | 49 +++++++++++++++---
.../recipes/nodes/TestPersistentNode.java | 59 +++++++++++++++++++++-
2 files changed, 99 insertions(+), 9 deletions(-)
diff --git
a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentNode.java
b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentNode.java
index f7b4653..72db454 100644
---
a/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentNode.java
+++
b/curator-recipes/src/main/java/org/apache/curator/framework/recipes/nodes/PersistentNode.java
@@ -72,9 +72,11 @@ public class PersistentNode implements Closeable
private final long ttl;
private final AtomicReference<byte[]> data = new AtomicReference<byte[]>();
private final AtomicReference<State> state = new
AtomicReference<State>(State.LATENT);
- private final AtomicBoolean authFailure = new AtomicBoolean(false);
+ private volatile boolean authFailure;
+ private volatile boolean parentCreationFailure;
private final BackgroundCallback backgroundCallback;
private final boolean useProtection;
+ private final boolean useParentCreation;
private final
AtomicReference<CreateModable<ACLBackgroundPathAndBytesable<String>>>
createMethod = new
AtomicReference<CreateModable<ACLBackgroundPathAndBytesable<String>>>(null);
private final StandardListenerManager<PersistentNodeListener> listeners =
StandardListenerManager.standard();
private final CuratorWatcher watcher = new CuratorWatcher()
@@ -140,7 +142,7 @@ public class PersistentNode implements Closeable
else if ( event.getResultCode() ==
KeeperException.Code.NOAUTH.intValue() )
{
log.warn("Client does not have authorisation to write node at
path {}", event.getPath());
- authFailure.set(true);
+ authFailure = true;
}
}
};
@@ -175,7 +177,19 @@ public class PersistentNode implements Closeable
*/
public PersistentNode(CuratorFramework givenClient, final CreateMode mode,
boolean useProtection, final String basePath, byte[] initData)
{
- this(givenClient, mode, useProtection, basePath, initData, -1);
+ this(givenClient, mode, useProtection, basePath, initData, -1, true);
+ }
+
+ /**
+ * @param givenClient client instance
+ * @param mode creation mode
+ * @param useProtection if true, call {@link
CreateBuilder#withProtection()}
+ * @param basePath the base path for the node
+ * @param initData data for the node
+ * @param useParentCreation if true, call {@link
CreateBuilder#creatingParentContainersIfNeeded()}
+ */
+ public PersistentNode(CuratorFramework givenClient, final CreateMode mode,
boolean useProtection, final String basePath, byte[] initData, boolean
useParentCreation) {
+ this(givenClient, mode, useProtection, basePath, initData, -1,
useParentCreation);
}
/**
@@ -185,10 +199,12 @@ public class PersistentNode implements Closeable
* @param basePath the base path for the node
* @param initData data for the node
* @param ttl for ttl modes, the ttl to use
+ * @param useParentCreation if true, call {@link
CreateBuilder#creatingParentContainersIfNeeded()}
*/
- public PersistentNode(CuratorFramework givenClient, final CreateMode mode,
boolean useProtection, final String basePath, byte[] initData, long ttl)
+ public PersistentNode(CuratorFramework givenClient, final CreateMode mode,
boolean useProtection, final String basePath, byte[] initData, long ttl,
boolean useParentCreation)
{
this.useProtection = useProtection;
+ this.useParentCreation = useParentCreation;
this.client = Preconditions.checkNotNull(givenClient, "client cannot
be null").newWatcherRemoveCuratorFramework();
this.basePath = PathUtils.validatePath(basePath);
this.mode = Preconditions.checkNotNull(mode, "mode cannot be null");
@@ -255,12 +271,17 @@ public class PersistentNode implements Closeable
else if ( event.getResultCode() ==
KeeperException.Code.NOAUTH.intValue() )
{
log.warn("Client does not have authorisation to create node at
path {}", event.getPath());
- authFailure.set(true);
+ authFailure = true;
+ return;
+ } else if ( event.getResultCode() ==
KeeperException.Code.NONODE.intValue() )
+ {
+ log.warn("Client cannot create parent hierarchy for path {} with
useParentCreation set to {}", event.getPath(), useParentCreation);
+ parentCreationFailure = true;
return;
}
if ( path != null )
{
- authFailure.set(false);
+ authFailure = false;
nodePath.set(path);
watchNode();
@@ -389,6 +410,7 @@ public class PersistentNode implements Closeable
{
data = Preconditions.checkNotNull(data, "data cannot be null");
Preconditions.checkState(nodePath.get() != null, "initial create has
not been processed. Call waitForInitialCreate() to ensure.");
+ Preconditions.checkState(!parentCreationFailure, "Failed to create
parent nodes.");
this.data.set(Arrays.copyOf(data, data.length));
if ( isActive() )
{
@@ -462,7 +484,12 @@ public class PersistentNode implements Closeable
if ( localCreateMethod == null )
{
CreateBuilderMain createBuilder = mode.isTTL() ?
client.create().withTtl(ttl) : client.create();
- CreateModable<ACLBackgroundPathAndBytesable<String>>
tempCreateMethod = useProtection ?
createBuilder.creatingParentContainersIfNeeded().withProtection() :
createBuilder.creatingParentContainersIfNeeded();
+ CreateModable<ACLBackgroundPathAndBytesable<String>>
tempCreateMethod;
+ if (useParentCreation) {
+ tempCreateMethod = useProtection ?
createBuilder.creatingParentContainersIfNeeded().withProtection() :
createBuilder.creatingParentContainersIfNeeded();
+ } else {
+ tempCreateMethod = useProtection ?
createBuilder.withProtection() : createBuilder;
+ }
createMethod.compareAndSet(null, tempCreateMethod);
localCreateMethod = createMethod.get();
}
@@ -543,6 +570,12 @@ public class PersistentNode implements Closeable
@VisibleForTesting
boolean isAuthFailure()
{
- return authFailure.get();
+ return authFailure;
}
+
+ @VisibleForTesting
+ boolean isParentCreationFailure() {
+ return parentCreationFailure;
+ }
+
}
diff --git
a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentNode.java
b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentNode.java
index 620c9cc..5ec20da 100644
---
a/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentNode.java
+++
b/curator-recipes/src/test/java/org/apache/curator/framework/recipes/nodes/TestPersistentNode.java
@@ -20,7 +20,9 @@ package org.apache.curator.framework.recipes.nodes;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@@ -101,6 +103,61 @@ public class TestPersistentNode extends BaseClassForTests
}
@Test
+ public void testCreationWithParentCreationOff() throws Exception {
+ Timing2 timing = new Timing2();
+ PersistentNode pen = null;
+ CuratorFramework client =
CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(),
timing.connection(), new RetryOneTime(1));
+
+ try {
+ client.start();
+ pen = new PersistentNode(client, CreateMode.PERSISTENT, false,
"/test/one/two", new byte[0], false);
+
pen.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds());
+ pen.start();
+ assertFalse(pen.waitForInitialCreate(timing.milliseconds(),
TimeUnit.MILLISECONDS));
+ assertTrue(pen.isParentCreationFailure());
+ } finally {
+ CloseableUtils.closeQuietly(pen);
+ CloseableUtils.closeQuietly(client);
+ }
+
+ }
+
+ @Test
+ public void testRecreationWithParentCreationOff() throws Exception {
+ final byte[] TEST_DATA = "hey".getBytes();
+ Timing2 timing = new Timing2();
+ PersistentNode pen = null;
+ CuratorFramework client =
CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(),
timing.connection(), new RetryOneTime(1));
+
+ try {
+ client.start();
+ client.create().creatingParentsIfNeeded().forPath("/test/one");
+ pen = new PersistentNode(client, CreateMode.EPHEMERAL, false,
"/test/one/two", TEST_DATA, false);
+
pen.debugWaitMsForBackgroundBeforeClose.set(timing.forSleepingABit().milliseconds());
+ pen.start();
+ assertTrue(pen.waitForInitialCreate(timing.milliseconds(),
TimeUnit.MILLISECONDS));
+ assertFalse(pen.isParentCreationFailure());
+ client.delete().deletingChildrenIfNeeded().forPath("/test/one");
+ timing.sleepABit();
+
+ // persistent node should not be able to recreate itself as the
lazy parent creation is disabled
+ assertNull(client.checkExists().forPath("/test/one/two"));
+ assertTrue(pen.isParentCreationFailure());
+ PersistentNode finalPen = pen;
+ assertThrows(IllegalStateException.class, () ->
finalPen.setData(new byte[0]));
+
+ // The persistent node data should still be the initial one
+ assertArrayEquals(TEST_DATA, pen.getData());
+
+ } finally {
+ CloseableUtils.closeQuietly(pen);
+ CloseableUtils.closeQuietly(client);
+ }
+
+
+ }
+
+ @Test
public void testQuickClose() throws Exception
{
Timing timing = new Timing();
@@ -145,7 +202,7 @@ public class TestPersistentNode extends BaseClassForTests
CloseableUtils.closeQuietly(client);
}
}
-
+
@Test
public void testEphemeralSequentialWithProtectionReconnection() throws
Exception
{