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 ad197952 CURATOR-719: Fix orSetData for parallel create calls (#510)
ad197952 is described below

commit ad1979520a923cc6ad3e24fc99a509b36338c5eb
Author: Houston Putman <[email protected]>
AuthorDate: Thu Dec 12 06:02:36 2024 -0600

    CURATOR-719: Fix orSetData for parallel create calls (#510)
---
 .../curator/framework/imps/CreateBuilderImpl.java  | 37 +++++----
 .../apache/curator/framework/imps/TestCreate.java  | 95 ++++++++++++++++++++--
 2 files changed, 109 insertions(+), 23 deletions(-)

diff --git 
a/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java
 
b/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java
index fa208141..5487f856 100644
--- 
a/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java
+++ 
b/curator-framework/src/main/java/org/apache/curator/framework/imps/CreateBuilderImpl.java
@@ -1147,23 +1147,26 @@ public class CreateBuilderImpl
 
                 if (createdPath == null) {
                     try {
-                        if (failBeforeNextCreateForTesting) {
-                            failBeforeNextCreateForTesting = false;
-                            throw new 
KeeperException.ConnectionLossException();
-                        }
-                        createdPath = client.getZooKeeper().create(path, data, 
aclList, createMode, storingStat, ttl);
-                    } catch (KeeperException.NoNodeException e) {
-                        if (createParentsIfNeeded) {
-                            ZKPaths.mkdirs(
-                                    client.getZooKeeper(),
-                                    path,
-                                    false,
-                                    acling.getACLProviderForParents(),
-                                    createParentsAsContainers);
-                            createdPath = client.getZooKeeper()
-                                    .create(path, data, 
acling.getAclList(path), createMode, storingStat, ttl);
-                        } else {
-                            throw e;
+                        try {
+                            if (failBeforeNextCreateForTesting) {
+                                failBeforeNextCreateForTesting = false;
+                                throw new 
KeeperException.ConnectionLossException();
+                            }
+                            createdPath =
+                                    client.getZooKeeper().create(path, data, 
aclList, createMode, storingStat, ttl);
+                        } catch (KeeperException.NoNodeException e) {
+                            if (createParentsIfNeeded) {
+                                ZKPaths.mkdirs(
+                                        client.getZooKeeper(),
+                                        path,
+                                        false,
+                                        acling.getACLProviderForParents(),
+                                        createParentsAsContainers);
+                                createdPath = client.getZooKeeper()
+                                        .create(path, data, 
acling.getAclList(path), createMode, storingStat, ttl);
+                            } else {
+                                throw e;
+                            }
                         }
                     } catch (KeeperException.NodeExistsException e) {
                         if (setDataIfExists) {
diff --git 
a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCreate.java
 
b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCreate.java
index 56be2a7b..4fccd4c6 100644
--- 
a/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCreate.java
+++ 
b/curator-framework/src/test/java/org/apache/curator/framework/imps/TestCreate.java
@@ -24,7 +24,11 @@ import static org.junit.jupiter.api.Assertions.*;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
+import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.curator.framework.CuratorFramework;
@@ -33,6 +37,7 @@ import org.apache.curator.framework.api.ACLProvider;
 import org.apache.curator.framework.api.BackgroundCallback;
 import org.apache.curator.framework.api.CreateBuilderMain;
 import org.apache.curator.framework.api.CuratorEvent;
+import org.apache.curator.framework.api.PathAndBytesable;
 import org.apache.curator.retry.RetryOneTime;
 import org.apache.curator.test.BaseClassForTests;
 import org.apache.curator.utils.CloseableUtils;
@@ -365,18 +370,37 @@ public class TestCreate extends BaseClassForTests {
     }
 
     private void check(
-            CuratorFramework client, CreateBuilderMain builder, String path, 
byte[] data, boolean expectedSuccess)
+            CuratorFramework client,
+            PathAndBytesable<String> builder,
+            String path,
+            byte[] data,
+            boolean expectedSuccess)
+            throws Exception {
+        check(
+                client,
+                builder,
+                path,
+                data,
+                0,
+                (expectedSuccess) ? KeeperException.Code.OK : 
KeeperException.Code.NODEEXISTS);
+    }
+
+    private void check(
+            CuratorFramework client,
+            PathAndBytesable<String> builder,
+            String path,
+            byte[] data,
+            int expectedVersion,
+            KeeperException.Code expectedCode)
             throws Exception {
-        int expectedCode =
-                (expectedSuccess) ? KeeperException.Code.OK.intValue() : 
KeeperException.Code.NODEEXISTS.intValue();
         try {
             builder.forPath(path, data);
-            assertEquals(expectedCode, KeeperException.Code.OK.intValue());
+            assertEquals(expectedCode, KeeperException.Code.OK);
             Stat stat = new Stat();
             byte[] actualData = 
client.getData().storingStatIn(stat).forPath(path);
-            assertTrue(IdempotentUtils.matches(0, data, stat.getVersion(), 
actualData));
+            assertTrue(IdempotentUtils.matches(expectedVersion, data, 
stat.getVersion(), actualData));
         } catch (KeeperException e) {
-            assertEquals(expectedCode, e.getCode());
+            assertEquals(expectedCode.intValue(), e.getCode());
         }
     }
 
@@ -540,6 +564,65 @@ public class TestCreate extends BaseClassForTests {
         }
     }
 
+    /**
+     * Tests all cases of create().orSetData()
+     */
+    @Test
+    public void testOrSetData() throws Exception {
+        CuratorFramework client = createClient(new DefaultACLProvider());
+        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 5, 
TimeUnit.SECONDS, new LinkedBlockingQueue<>());
+        try {
+            client.start();
+
+            Stat stat = new Stat();
+
+            String path = "/idpcreate";
+            String pathWithParents1 = "/idpcreate/1/a/b/c/d";
+            String pathWithParents2 = "/idpcreate/2/a/b/c/d";
+            byte[] data1 = new byte[] {1, 2, 3};
+            byte[] data2 = new byte[] {4, 5, 6};
+
+            // first and second create should succeed with the same path and 
different data
+            check(client, client.create().orSetData(), path, data1, 0, 
KeeperException.Code.OK);
+            check(client, client.create().orSetData(), path, data2, 1, 
KeeperException.Code.OK);
+            check(client, client.create(), path, data2, false);
+
+            // without creatingParentsIfNeeded, it should fail
+            check(client, client.create().orSetData(), pathWithParents1, 
data1, 0, KeeperException.Code.NONODE);
+
+            // with creatingParentsIfNeeded, it should succeed and succeed a 
second time as well
+            check(
+                    client,
+                    client.create().orSetData().creatingParentsIfNeeded(),
+                    pathWithParents1,
+                    data1,
+                    0,
+                    KeeperException.Code.OK);
+            check(client, client.create().orSetData(), pathWithParents1, 
data2, 1, KeeperException.Code.OK);
+
+            // Check that calling the same create().orSetData() in parallel is 
ok
+            Callable<Exception> setData = () -> {
+                try {
+                    
client.create().orSetData().creatingParentsIfNeeded().forPath(pathWithParents2, 
data2);
+                } catch (Exception e) {
+                    return e;
+                }
+                return null;
+            };
+            Future<Exception> f1 = executor.submit(setData);
+            Future<Exception> f2 = executor.submit(setData);
+            assertNull(f1.get(), "Exception thrown during 1st parallel 
create");
+            assertNull(f2.get(), "Exception thrown during 2nd parallel 
create");
+
+            byte[] foundData = 
client.getData().storingStatIn(stat).forPath(pathWithParents2);
+            assertArrayEquals(data2, foundData, "Data does not match after 
parallel creates");
+            assertEquals(1, stat.getVersion(), "Version should be 1 after 2 
parallel creates");
+        } finally {
+            CloseableUtils.closeQuietly(client);
+            executor.shutdownNow();
+        }
+    }
+
     @Test
     public void testCreateProtectedUtils() throws Exception {
         try (CuratorFramework client = CuratorFrameworkFactory.builder()

Reply via email to