Skip to content

Commit fb78e23

Browse files
kezhuwtisonkun
andauthored
CURATOR-710: Fix leaking watch in EnsembleTracker (#508)
CURATOR-667(#474) fixes asynchronous event path for `getConfig` to "/zookeeper/config" by using `CuratorFramework::usingNamespace(null)` to fetch data. It causes watcher not registering to possible `WatcherRemovalManager`, so leaking in `WatcherRemoveCuratorFramework::removeWatchers`. Signed-off-by: tison <wander4096@gmail.com> Co-authored-by: tison <wander4096@gmail.com>
1 parent 8eb6f9a commit fb78e23

File tree

4 files changed

+74
-9
lines changed

4 files changed

+74
-9
lines changed

curator-framework/src/main/java/org/apache/curator/framework/imps/GetConfigBuilderImpl.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ public class GetConfigBuilderImpl
3939
private Stat stat;
4040

4141
public GetConfigBuilderImpl(CuratorFrameworkImpl client) {
42-
this.client = (CuratorFrameworkImpl) client.usingNamespace(null);
43-
backgrounding = new Backgrounding();
44-
watching = new Watching(this.client);
42+
this(client, new Backgrounding(), null, null);
4543
}
4644

4745
public GetConfigBuilderImpl(CuratorFrameworkImpl client, Backgrounding backgrounding, Watcher watcher, Stat stat) {

curator-framework/src/main/java/org/apache/curator/framework/imps/WatcherRemovalFacade.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@ class WatcherRemovalFacade extends CuratorFrameworkImpl implements WatcherRemove
3737
private final WatcherRemovalManager removalManager;
3838

3939
WatcherRemovalFacade(CuratorFrameworkImpl client) {
40+
this(client, new WatcherRemovalManager(client));
41+
}
42+
43+
private WatcherRemovalFacade(CuratorFrameworkImpl client, WatcherRemovalManager removalManager) {
4044
super(client);
4145
this.client = client;
42-
removalManager = new WatcherRemovalManager(client);
46+
this.removalManager = removalManager;
4347
}
4448

4549
@Override
@@ -73,7 +77,8 @@ public CuratorFramework nonNamespaceView() {
7377

7478
@Override
7579
public CuratorFramework usingNamespace(String newNamespace) {
76-
return client.usingNamespace(newNamespace);
80+
final CuratorFrameworkImpl newClient = (CuratorFrameworkImpl) client.usingNamespace(newNamespace);
81+
return new WatcherRemovalFacade(newClient, removalManager);
7782
}
7883

7984
@Override

curator-framework/src/main/java/org/apache/curator/framework/imps/Watching.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,8 @@ void commitWatcher(int rc, boolean isExists) {
8585
doCommit = (rc == KeeperException.Code.OK.intValue());
8686
}
8787

88-
if (doCommit && (namespaceWatcher != null)) {
89-
if (client.getWatcherRemovalManager() != null) {
90-
client.getWatcherRemovalManager().add(namespaceWatcher);
91-
}
88+
if (doCommit && namespaceWatcher != null && client.getWatcherRemovalManager() != null) {
89+
client.getWatcherRemovalManager().add(namespaceWatcher);
9290
}
9391
}
9492
}

curator-framework/src/test/java/org/apache/curator/framework/imps/TestWatcherRemovalManager.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import static org.junit.jupiter.api.Assertions.assertEquals;
2323
import static org.junit.jupiter.api.Assertions.assertTrue;
2424
import static org.junit.jupiter.api.Assertions.fail;
25+
import java.util.Collections;
2526
import java.util.List;
2627
import java.util.concurrent.Callable;
2728
import java.util.concurrent.CountDownLatch;
@@ -34,12 +35,27 @@
3435
import org.apache.curator.test.Timing;
3536
import org.apache.curator.test.WatchersDebug;
3637
import org.apache.curator.test.compatibility.CuratorTestBase;
38+
import org.apache.curator.utils.DebugUtils;
3739
import org.apache.zookeeper.KeeperException;
3840
import org.apache.zookeeper.WatchedEvent;
3941
import org.apache.zookeeper.Watcher;
42+
import org.apache.zookeeper.ZooDefs;
43+
import org.apache.zookeeper.server.quorum.QuorumPeer;
44+
import org.apache.zookeeper.server.quorum.flexible.QuorumMaj;
45+
import org.junit.jupiter.api.BeforeEach;
4046
import org.junit.jupiter.api.Test;
4147

4248
public class TestWatcherRemovalManager extends CuratorTestBase {
49+
private static final String superUserPasswordDigest = "curator-test:zghsj3JfJqK7DbWf0RQ1BgbJH9w="; // ran from
50+
private static final String superUserPassword = "curator-test";
51+
52+
@BeforeEach
53+
@Override
54+
public void setup() throws Exception {
55+
System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", superUserPasswordDigest);
56+
super.setup();
57+
}
58+
4359
@Test
4460
public void testSameWatcherDifferentPaths1Triggered() throws Exception {
4561
CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));
@@ -302,6 +318,54 @@ public void testBasicNamespace3() throws Exception {
302318
}
303319
}
304320

321+
@Test
322+
public void testEnsembleTracker() throws Exception {
323+
// given: client with ensemble tracker
324+
CuratorFramework client = CuratorFrameworkFactory.builder()
325+
.connectString(server.getConnectString())
326+
.retryPolicy(new RetryOneTime(1))
327+
.namespace("hey")
328+
.ensembleTracker(true)
329+
.authorization("digest", superUserPassword.getBytes())
330+
.build();
331+
try {
332+
client.start();
333+
334+
// We are using standalone, so "/zookeeper/config" will be empty.
335+
// So let's set it directly.
336+
QuorumMaj quorumMaj = new QuorumMaj(Collections.singletonMap(
337+
1L,
338+
new QuorumPeer.QuorumServer(1, "127.0.0.1:2182:2183:participant;" + server.getConnectString())));
339+
quorumMaj.setVersion(1);
340+
client.usingNamespace(null)
341+
.setData()
342+
.forPath(ZooDefs.CONFIG_NODE, quorumMaj.toString().getBytes());
343+
344+
// when: zookeeper config node data fetched
345+
while (client.getCurrentConfig().getVersion() == 0) {
346+
Thread.sleep(100);
347+
}
348+
349+
// then: the watcher must be attached
350+
assertEquals(
351+
1,
352+
WatchersDebug.getDataWatches(client.getZookeeperClient().getZooKeeper())
353+
.size());
354+
355+
// when: ensemble tracker closed
356+
System.setProperty(DebugUtils.PROPERTY_REMOVE_WATCHERS_IN_FOREGROUND, "true");
357+
((CuratorFrameworkImpl) client).getEnsembleTracker().close();
358+
359+
// then: the watcher must be removed
360+
assertEquals(
361+
0,
362+
WatchersDebug.getDataWatches(client.getZookeeperClient().getZooKeeper())
363+
.size());
364+
} finally {
365+
TestCleanState.closeAndTestClean(client);
366+
}
367+
}
368+
305369
@Test
306370
public void testSameWatcher() throws Exception {
307371
CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(1));

0 commit comments

Comments
 (0)