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 7efa1277735..f05146bbd43 100644 --- a/zookeeper-server/src/main/java/org/apache/zookeeper/ZKWatchManager.java +++ b/zookeeper-server/src/main/java/org/apache/zookeeper/ZKWatchManager.java @@ -446,6 +446,11 @@ private void addPersistentWatches(String clientPath, Watcher.Event.EventType typ synchronized (persistentWatches) { addTo(persistentWatches.get(clientPath), result); } + // The semantics of persistent recursive watch promise no child events on descendant nodes. But this + // could not be achieved in server side as there could be standard child watches on descendants of node + // being watched in persistent recursive mode. This means server could fire child events on descendant + // nodes due to standard child watches on these nodes. So we have to filter out child events for persistent + // recursive watches on client side. if (type == Watcher.Event.EventType.NodeChildrenChanged) { return; } 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 6acb35ff775..e74ee2fd683 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 @@ -121,13 +121,17 @@ public void testNoChildEvents() throws Exception { zk.addWatch("/", persistentWatcher, PERSISTENT_RECURSIVE); - zk.getChildren("/a", true); + BlockingQueue 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()); } }