This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/helix.git
The following commit(s) were added to refs/heads/master by this push:
new 695b1fd3b Upgrade ZooKeeper from 3.6.3 to 3.9.3 (#3093)
695b1fd3b is described below
commit 695b1fd3bfc8bb1b91e37de2827738dcd56260b9
Author: Junkai Xue <[email protected]>
AuthorDate: Fri Nov 6 01:25:27 2026 -0800
Upgrade ZooKeeper from 3.6.3 to 3.9.3 (#3093)
ZOOKEEPER-4712 (Follower/Observer shutdown does not stop the sync processor,
which can leave the data tree inconsistent) affects every release Helix
could
pick from the 3.6, 3.7 and 3.8 lines; the fix only shipped in 3.9.3 and
3.10.0. 3.9.3 is therefore the smallest bump that contains it. The 3.6 line
has also been end of life since December 2022.
No Helix API changes. Every ZooKeeper type that Helix exposes (Stat, Op,
OpResult, ACL, CreateMode, Watcher, KeeperException, ZooKeeper) is unchanged
between the two versions, and all modules compile without source changes,
including the embedded ZkServer and the tools that read transaction logs and
snapshots. Paginated getChildren is still not part of the ZooKeeper client,
so
ZkClient keeps using the regular API. The OSGi Import-Package range for
org.apache.zookeeper in zookeeper-api and helix-rest moves to [3.9,3.10).
Dependency changes: 3.9.3 drops the log4j 1 binding (logback is an optional
dependency and is not pulled in), brings netty 4.1.113 and commons-io 2.17
(Helix already manages both), and newly adds
netty-tcnative-boringssl-static,
a multi-platform native OpenSSL binding only used for TLS between client and
server. Helix does not configure ZooKeeper TLS, so that artifact is excluded
to keep the dependency footprint the same as before. The checked-in ivy
files
and the meta-client KeeperException code mapping are updated for 3.9.3; the
two codes added since 3.6 (QUOTAEXCEEDED, THROTTLEDOP) are API errors and
map to DB_USER_ERROR, which is what the default branch already produced.
Behavior change verified by tests: ZooKeeper 3.9.0 fixed the server so that
a
one time watch on a path no longer replaces a persistent watch on the same
path (ZOOKEEPER-4466) and persistent recursive watches no longer receive
NodeChildrenChanged (ZOOKEEPER-4475). TestZooKeeperConnection asserted the
old behavior (persistent watchers silently missing events after a one time
watch was added) and now asserts that no event is missed. ZkClient itself is
unaffected: in persist mode it registers child subscriptions with
AddWatchMode.PERSISTENT, refuses one time watches, and never relied on
NodeChildrenChanged for recursive persistent listeners.
ZkTestHelper.getZkWatch (helix-core and zookeeper-api test helpers) read the
client's watch registry through the ZooKeeper#watchManager field, which 3.7
moved into ClientCnxn behind the package-private getWatchManager(); the
helper now calls that accessor.
Tests run locally against 3.9.3: the whole zookeeper-api module, and in
helix-core the ZK client, session, watcher, reconnect, flapping, connection
loss, data accessor, cache, bucket accessor, size limit, admin and ZkCopy
test classes.
---
helix-core/helix-core-2.0.2-SNAPSHOT.ivy | 2 +-
.../test/java/org/apache/helix/ZkTestHelper.java | 17 ++++++------
helix-rest/pom.xml | 2 +-
.../metaclient/impl/zk/util/ZkMetaClientUtil.java | 6 ++++-
zookeeper-api/pom.xml | 10 ++++++--
.../zookeeper/impl/TestZooKeeperConnection.java | 30 ++++++++++++----------
.../apache/helix/zookeeper/impl/ZkTestHelper.java | 17 ++++++------
zookeeper-api/zookeeper-api-2.0.2-SNAPSHOT.ivy | 2 +-
8 files changed, 49 insertions(+), 37 deletions(-)
diff --git a/helix-core/helix-core-2.0.2-SNAPSHOT.ivy
b/helix-core/helix-core-2.0.2-SNAPSHOT.ivy
index fff0a3652..0d1e248a3 100755
--- a/helix-core/helix-core-2.0.2-SNAPSHOT.ivy
+++ b/helix-core/helix-core-2.0.2-SNAPSHOT.ivy
@@ -52,7 +52,7 @@ under the License.
<dependency org="org.apache.logging.log4j" name="log4j-slf4j-impl"
rev="2.17.1" force="true"
conf="compile->compile(*),master(*);runtime->runtime(*)">
<artifact name="log4j-slf4j-impl" ext="jar"/>
</dependency>
- <dependency org="org.apache.zookeeper" name="zookeeper" rev="3.6.3"
conf="compile->compile(default);runtime->runtime(default);default->default"/>
+ <dependency org="org.apache.zookeeper" name="zookeeper" rev="3.9.3"
conf="compile->compile(default);runtime->runtime(default);default->default"/>
<dependency org="com.fasterxml.jackson.core"
name="jackson-databind" rev="2.12.6.1"
conf="compile->compile(default);runtime->runtime(default);default->default"/>
<dependency org="com.fasterxml.jackson.core" name="jackson-core"
rev="2.12.6"
conf="compile->compile(default);runtime->runtime(default);default->default"/>
<dependency org="commons-io" name="commons-io" rev="2.11.0"
conf="compile->compile(default);runtime->runtime(default);default->default"/>
diff --git a/helix-core/src/test/java/org/apache/helix/ZkTestHelper.java
b/helix-core/src/test/java/org/apache/helix/ZkTestHelper.java
index 74158bc45..3632ebfa1 100644
--- a/helix-core/src/test/java/org/apache/helix/ZkTestHelper.java
+++ b/helix-core/src/test/java/org/apache/helix/ZkTestHelper.java
@@ -399,24 +399,23 @@ public class ZkTestHelper {
ZkConnection connection = ((ZkConnection) zkClient.getConnection());
ZooKeeper zk = connection.getZookeeper();
- java.lang.reflect.Field field = getField(zk.getClass(), "watchManager");
- field.setAccessible(true);
- Object watchManager = field.get(zk);
+ // Since ZooKeeper 3.7 the client watch manager lives in ClientCnxn and is
reached through the
+ // package-private ZooKeeper#getWatchManager() instead of a field.
+ java.lang.reflect.Method getWatchManager =
ZooKeeper.class.getDeclaredMethod("getWatchManager");
+ getWatchManager.setAccessible(true);
+ Object watchManager = getWatchManager.invoke(zk);
java.lang.reflect.Field field2 = getField(watchManager.getClass(),
"dataWatches");
field2.setAccessible(true);
- HashMap<String, Set<Watcher>> dataWatches =
- (HashMap<String, Set<Watcher>>) field2.get(watchManager);
+ Map<String, Set<Watcher>> dataWatches = (Map<String, Set<Watcher>>)
field2.get(watchManager);
field2 = getField(watchManager.getClass(), "existWatches");
field2.setAccessible(true);
- HashMap<String, Set<Watcher>> existWatches =
- (HashMap<String, Set<Watcher>>) field2.get(watchManager);
+ Map<String, Set<Watcher>> existWatches = (Map<String, Set<Watcher>>)
field2.get(watchManager);
field2 = getField(watchManager.getClass(), "childWatches");
field2.setAccessible(true);
- HashMap<String, Set<Watcher>> childWatches =
- (HashMap<String, Set<Watcher>>) field2.get(watchManager);
+ Map<String, Set<Watcher>> childWatches = (Map<String, Set<Watcher>>)
field2.get(watchManager);
lists.put("dataWatches", new ArrayList<>(dataWatches.keySet()));
lists.put("existWatches", new ArrayList<>(existWatches.keySet()));
diff --git a/helix-rest/pom.xml b/helix-rest/pom.xml
index ab50e4524..c10724aef 100644
--- a/helix-rest/pom.xml
+++ b/helix-rest/pom.xml
@@ -51,7 +51,7 @@
org.slf4j*;version="[1.7,2)",
org.apache.logging.log4j*;version="[2.17,3)",
org.apache.logging.slf4j*;version="[2.17,3)",
- org.apache.zookeeper*;version="[3.6,3.7)",
+ org.apache.zookeeper*;version="[3.9,3.10)",
org.apache.commons.io*;version="[1.4,2)",
*
</osgi.import>
diff --git
a/meta-client/src/main/java/org/apache/helix/metaclient/impl/zk/util/ZkMetaClientUtil.java
b/meta-client/src/main/java/org/apache/helix/metaclient/impl/zk/util/ZkMetaClientUtil.java
index aee0c698a..f1693dbba 100644
---
a/meta-client/src/main/java/org/apache/helix/metaclient/impl/zk/util/ZkMetaClientUtil.java
+++
b/meta-client/src/main/java/org/apache/helix/metaclient/impl/zk/util/ZkMetaClientUtil.java
@@ -277,7 +277,7 @@ public class ZkMetaClientUtil {
/**
* This function translate and group Zk exception code to metaclient code.
- * It currently includes all ZK code on 3.6.3.
+ * It currently includes all ZK code on 3.9.3.
*/
public static MetaClientException.ReturnCode
translateZooKeeperCodeToMetaClientCode(
KeeperException.Code zkCode) {
@@ -341,6 +341,10 @@ public class ZkMetaClientUtil {
case NOTREADONLY:
case EPHEMERALONLOCALSESSION:
case RECONFIGDISABLED:
+ // QUOTAEXCEEDED (the znode quota is exhausted) and THROTTLEDOP (the
server is overloaded and did
+ // not execute the operation at all) were added after 3.6; both are API
errors in ZK.
+ case QUOTAEXCEEDED:
+ case THROTTLEDOP:
return MetaClientException.ReturnCode.DB_USER_ERROR;
/*
diff --git a/zookeeper-api/pom.xml b/zookeeper-api/pom.xml
index c5e24bb39..b58f48ae4 100644
--- a/zookeeper-api/pom.xml
+++ b/zookeeper-api/pom.xml
@@ -45,7 +45,7 @@
<properties>
<osgi.import>
org.slf4j*;version="[1.7,2)",
- org.apache.zookeeper*;version="[3.6,3.7)",
+ org.apache.zookeeper*;version="[3.9,3.10)",
*
</osgi.import>
<osgi.export>org.apache.helix.zookeeper*;version="${project.version};-noimport:=true</osgi.export>
@@ -64,7 +64,7 @@
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
- <version>3.6.3</version>
+ <version>3.9.3</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
@@ -78,6 +78,12 @@
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
+ <!-- Native OpenSSL binding for all platforms, only used for
client/server TLS which Helix
+ does not configure. -->
+ <exclusion>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-tcnative-boringssl-static</artifactId>
+ </exclusion>
</exclusions>
</dependency>
<dependency>
diff --git
a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/TestZooKeeperConnection.java
b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/TestZooKeeperConnection.java
index 0ea8524db..0454167e5 100644
---
a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/TestZooKeeperConnection.java
+++
b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/TestZooKeeperConnection.java
@@ -27,6 +27,9 @@ public class TestZooKeeperConnection extends ZkTestBase {
1. Register a persist watcher on a path and create 100 children Znode, edit
the ZNode for 100 times.
Expecting 200 events.
2. register a one time listener on the path. Make the same change and count
the total number of event.
+ Since ZooKeeper 3.9.0 (ZOOKEEPER-4466) a one time watch on the same path no
longer replaces the
+ persistent watch on the server, so the persistent watcher keeps receiving
every event. Before that
+ fix it silently missed events, which is why ZkClient refuses one time
watches in persist mode.
*/
@Test
@@ -46,18 +49,18 @@ public class TestZooKeeperConnection extends ZkTestBase {
Assert.assertTrue(countDownLatch.await(50000, TimeUnit.MILLISECONDS));
// register a one time listener on the path. Count the total number of
event.
- // ZK will over write the persist watcher and only trigger event once for
child and data change.
+ // The one time watch coexists with the persist watcher (ZOOKEEPER-4466),
so the persist
+ // watcher still gets all 200 data change and 200 child change events.
_zk.readData(path, null, true);
_zk.getChildren(path, true);
for (int i=0; i<200; ++i) {
_zk.writeData(path, ("datat"+i).getBytes(), -1);
_zk.create(path+"/c2_" +i, null, ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
- System.out.println("testPersistWatcher: rafter register one time listener,
original listener received event count: " + get_count[0]);
- // total number of event is 400. We will miss event now
- Assert.assertTrue(TestHelper.verify(() -> {
- return (get_count[0].get() >= 202 & get_count[0].get() < 400);
- }, TestHelper.WAIT_DURATION));
+ System.out.println("testPersistWatcher: after register one time listener,
original listener received event count: " + get_count[0]);
+ // total number of events is 200 + 400 = 600 and none of them is missed
+ Assert.assertTrue(TestHelper.verify(() -> get_count[0].get() == 600,
TestHelper.WAIT_DURATION),
+ "Persist watcher received " + get_count[0].get() + " events, expected
600");
zkClient.close();
}
@@ -70,8 +73,9 @@ public class TestZooKeeperConnection extends ZkTestBase {
IZkConnection _zk = zkClient.getConnection();
String path="/testRecursivePersistWatcher";
_zk.create(path, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
- // register a persist listener on a path, change the ZNode 100 times,
create 100 child ZNode,
- // and expecting 200 events
+ // register a recursive persist listener on a path, change the ZNode 100
times, create 100 child
+ // ZNode and 100 grandchild ZNode, and expecting 300 events. A recursive
persist watcher only gets
+ // NodeDataChanged/NodeCreated/NodeDeleted, no NodeChildrenChanged
(ZOOKEEPER-4475).
_zk.addWatch(path, watcher1, AddWatchMode.PERSISTENT_RECURSIVE);
for (int i=0; i<count; ++i) {
_zk.writeData(path, "datat".getBytes(), -1);
@@ -81,7 +85,8 @@ public class TestZooKeeperConnection extends ZkTestBase {
Assert.assertTrue(countDownLatch2.await(50000, TimeUnit.MILLISECONDS));
// register a one time listener on the path. Count the total number of
event.
- // ZK will over write the persist watcher and only trigger event once for
child and data change.
+ // The one time watch coexists with the persist watcher (ZOOKEEPER-4466),
so the persist
+ // watcher still gets all 200 data change and 200 child creation events.
_zk.readData(path, null, true);
_zk.getChildren(path, true);
for (int i=0; i<200; ++i) {
@@ -89,10 +94,9 @@ public class TestZooKeeperConnection extends ZkTestBase {
_zk.create(path+"/c2_" +i, null, ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
}
System.out.println("testRecursivePersistWatcherWithOneTimeWatcher: after
register one time listener, original listener received event count: " +
get_count[0]);
- // total number of event is 500. We will miss event now
- Assert.assertTrue(TestHelper.verify(() -> {
- return (get_count[0].get() >= 302 && get_count[0].get() < 500);
- }, TestHelper.WAIT_DURATION));
+ // total number of events is 300 + 400 = 700 and none of them is missed
+ Assert.assertTrue(TestHelper.verify(() -> get_count[0].get() == 700,
TestHelper.WAIT_DURATION),
+ "Recursive persist watcher received " + get_count[0].get() + " events,
expected 700");
zkClient.close();
}
diff --git
a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/ZkTestHelper.java
b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/ZkTestHelper.java
index 32e2b8acc..5624c9eca 100644
---
a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/ZkTestHelper.java
+++
b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/impl/ZkTestHelper.java
@@ -360,24 +360,23 @@ public class ZkTestHelper {
ZkConnection connection = ((ZkConnection) zkClient.getConnection());
ZooKeeper zk = connection.getZookeeper();
- java.lang.reflect.Field field = getField(zk.getClass(), "watchManager");
- field.setAccessible(true);
- Object watchManager = field.get(zk);
+ // Since ZooKeeper 3.7 the client watch manager lives in ClientCnxn and is
reached through the
+ // package-private ZooKeeper#getWatchManager() instead of a field.
+ java.lang.reflect.Method getWatchManager =
ZooKeeper.class.getDeclaredMethod("getWatchManager");
+ getWatchManager.setAccessible(true);
+ Object watchManager = getWatchManager.invoke(zk);
java.lang.reflect.Field field2 = getField(watchManager.getClass(),
"dataWatches");
field2.setAccessible(true);
- HashMap<String, Set<Watcher>> dataWatches =
- (HashMap<String, Set<Watcher>>) field2.get(watchManager);
+ Map<String, Set<Watcher>> dataWatches = (Map<String, Set<Watcher>>)
field2.get(watchManager);
field2 = getField(watchManager.getClass(), "existWatches");
field2.setAccessible(true);
- HashMap<String, Set<Watcher>> existWatches =
- (HashMap<String, Set<Watcher>>) field2.get(watchManager);
+ Map<String, Set<Watcher>> existWatches = (Map<String, Set<Watcher>>)
field2.get(watchManager);
field2 = getField(watchManager.getClass(), "childWatches");
field2.setAccessible(true);
- HashMap<String, Set<Watcher>> childWatches =
- (HashMap<String, Set<Watcher>>) field2.get(watchManager);
+ Map<String, Set<Watcher>> childWatches = (Map<String, Set<Watcher>>)
field2.get(watchManager);
lists.put("dataWatches", new ArrayList<>(dataWatches.keySet()));
lists.put("existWatches", new ArrayList<>(existWatches.keySet()));
diff --git a/zookeeper-api/zookeeper-api-2.0.2-SNAPSHOT.ivy
b/zookeeper-api/zookeeper-api-2.0.2-SNAPSHOT.ivy
index 1b581384f..cecff16bc 100644
--- a/zookeeper-api/zookeeper-api-2.0.2-SNAPSHOT.ivy
+++ b/zookeeper-api/zookeeper-api-2.0.2-SNAPSHOT.ivy
@@ -43,7 +43,7 @@ under the License.
<dependency org="org.apache.logging.log4j" name="log4j-slf4j-impl"
rev="2.17.1" force="true"
conf="compile->compile(*),master(*);runtime->runtime(*)">
<artifact name="log4j-slf4j-impl" ext="jar"/>
</dependency>
- <dependency org="org.apache.zookeeper" name="zookeeper" rev="3.6.3"
conf="compile->compile(default);runtime->runtime(default);default->default"/>
+ <dependency org="org.apache.zookeeper" name="zookeeper" rev="3.9.3"
conf="compile->compile(default);runtime->runtime(default);default->default"/>
<dependency org="com.fasterxml.jackson.core"
name="jackson-databind" rev="2.12.6.1" force="true"
conf="compile->compile(*),master(*);runtime->runtime(*)"/>
<dependency org="com.fasterxml.jackson.core"
name="jackson-core" rev="2.12.6" force="true"
conf="compile->compile(*),master(*);runtime->runtime(*)"/>
<dependency org="commons-cli" name="commons-cli" rev="1.2"
force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/>