diff options
author | Kezhu Wang <kezhuw@gmail.com> | 2023-02-22 17:23:00 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-22 10:23:00 +0100 |
commit | 255b0c9137a75635cf6e2c112672a045c58ce1cf (patch) | |
tree | f6042f078a80c1fb0beb0a21ac145aba6593c4c4 | |
parent | ac219cef2ad7999fc95c8fd4eb3b945ba37abd43 (diff) | |
download | zookeeper-255b0c9137a75635cf6e2c112672a045c58ce1cf.tar.gz |
ZOOKEEPER-4475: Fix NodeChildrenChanged delivered to recursive watcher (#1820)
The semantics of persistent recursive watch promise no child events on
descendant nodes. When there are standard child watches on descendants
of node being watches in persistent recursive mode, server will deliver
child events to client inevitably. So we have to filter out child events
for persistent recursive watches on client side.
-rw-r--r-- | zookeeper-server/src/main/java/org/apache/zookeeper/ZKWatchManager.java | 15 | ||||
-rw-r--r-- | zookeeper-server/src/test/java/org/apache/zookeeper/test/PersistentRecursiveWatcherTest.java | 21 |
2 files changed, 32 insertions, 4 deletions
diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/ZKWatchManager.java b/zookeeper-server/src/main/java/org/apache/zookeeper/ZKWatchManager.java index 95a07f0e7..9da424944 100644 --- a/zookeeper-server/src/main/java/org/apache/zookeeper/ZKWatchManager.java +++ b/zookeeper-server/src/main/java/org/apache/zookeeper/ZKWatchManager.java @@ -404,13 +404,13 @@ class ZKWatchManager implements ClientWatchManager { synchronized (existWatches) { addTo(existWatches.remove(clientPath), result); } - addPersistentWatches(clientPath, result); + addPersistentWatches(clientPath, type, result); break; case NodeChildrenChanged: synchronized (childWatches) { addTo(childWatches.remove(clientPath), result); } - addPersistentWatches(clientPath, result); + addPersistentWatches(clientPath, type, result); break; case NodeDeleted: synchronized (dataWatches) { @@ -427,7 +427,7 @@ class ZKWatchManager implements ClientWatchManager { synchronized (childWatches) { addTo(childWatches.remove(clientPath), result); } - addPersistentWatches(clientPath, result); + addPersistentWatches(clientPath, type, result); break; default: String errorMsg = String.format( @@ -442,10 +442,17 @@ class ZKWatchManager implements ClientWatchManager { return result; } - private void addPersistentWatches(String clientPath, Set<Watcher> result) { + private void addPersistentWatches(String clientPath, Watcher.Event.EventType type, Set<Watcher> result) { synchronized (persistentWatches) { addTo(persistentWatches.get(clientPath), result); } + // The semantics of persistent recursive watch promise no child events on descendant nodes. When there + // are standard child watches on descendants of node being watched in persistent recursive mode, server + // will deliver child events to client inevitably. So we have to filter out child events for persistent + // recursive watches on client side. + if (type == Watcher.Event.EventType.NodeChildrenChanged) { + return; + } synchronized (persistentRecursiveWatches) { for (String path : PathParentIterator.forAll(clientPath).asIterable()) { addTo(persistentRecursiveWatches.get(path), result); diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/PersistentRecursiveWatcherTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/PersistentRecursiveWatcherTest.java index 80d8c400c..e74ee2fd6 100644 --- a/zookeeper-server/src/test/java/org/apache/zookeeper/test/PersistentRecursiveWatcherTest.java +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/PersistentRecursiveWatcherTest.java @@ -115,6 +115,27 @@ public class PersistentRecursiveWatcherTest extends ClientBase { } @Test + public void testNoChildEvents() throws Exception { + try (ZooKeeper zk = createClient()) { + zk.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + + zk.addWatch("/", persistentWatcher, PERSISTENT_RECURSIVE); + + BlockingQueue<WatchedEvent> childEvents = new LinkedBlockingQueue<>(); + zk.getChildren("/a", childEvents::add); + + zk.create("/a/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + zk.create("/a/b/c", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); + + assertEvent(childEvents, Watcher.Event.EventType.NodeChildrenChanged, "/a"); + + assertEvent(events, Watcher.Event.EventType.NodeCreated, "/a/b"); + assertEvent(events, Watcher.Event.EventType.NodeCreated, "/a/b/c"); + assertTrue(events.isEmpty()); + } + } + + @Test public void testDisconnect() throws Exception { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { zk.addWatch("/a/b", persistentWatcher, PERSISTENT_RECURSIVE); |