void printZkListeners(ZkClient client) throws Exception { Map<String, Set<IZkDataListener>> datalisteners = ZkTestHelper.getZkDataListener(client); Map<String, Set<IZkChildListener>> childListeners = ZkTestHelper.getZkChildListener(client); System.out.println("dataListeners {"); for (String path : datalisteners.keySet()) { System.out.println("\t" + path + ": "); Set<IZkDataListener> set = datalisteners.get(path); for (IZkDataListener listener : set) { CallbackHandler handler = (CallbackHandler) listener; System.out.println("\t\t" + handler.getListener()); } } System.out.println("}"); System.out.println("childListeners {"); for (String path : childListeners.keySet()) { System.out.println("\t" + path + ": "); Set<IZkChildListener> set = childListeners.get(path); for (IZkChildListener listener : set) { CallbackHandler handler = (CallbackHandler) listener; System.out.println("\t\t" + handler.getListener()); } } System.out.println("}"); }
@Test public void testRemoveUserCbHandlerOnPathRemoval() throws Exception { String className = TestHelper.getTestClassName(); String methodName = TestHelper.getTestMethodName(); String clusterName = className + "_" + methodName; final int n = 3; final String zkAddr = ZK_ADDR; System.out.println("START " + clusterName + " at " + new Date(System.currentTimeMillis())); TestHelper.setupCluster( clusterName, zkAddr, 12918, "localhost", "TestDB", 1, // resource 32, // partitions n, // nodes 2, // replicas "MasterSlave", true); final ClusterControllerManager controller = new ClusterControllerManager(zkAddr, clusterName, "controller_0"); controller.syncStart(); MockParticipantManager[] participants = new MockParticipantManager[n]; for (int i = 0; i < n; i++) { String instanceName = "localhost_" + (12918 + i); participants[i] = new MockParticipantManager(zkAddr, clusterName, instanceName); participants[i].syncStart(); // register a controller listener on participant_0 if (i == 0) { // ZkHelixTestManager manager = participants[0].getManager(); MockParticipantManager manager = participants[0]; manager.addCurrentStateChangeListener( new CurrentStateChangeListener() { @Override public void onStateChange( String instanceName, List<CurrentState> statesInfo, NotificationContext changeContext) { // To change body of implemented methods use File | Settings | File Templates. // System.out.println(instanceName + " on current-state change, type: " + // changeContext.getType()); } }, manager.getInstanceName(), manager.getSessionId()); } } Boolean result = ClusterStateVerifier.verifyByZkCallback( new ClusterStateVerifier.BestPossAndExtViewZkVerifier(zkAddr, clusterName)); Assert.assertTrue(result); MockParticipantManager participantToExpire = participants[0]; String oldSessionId = participantToExpire.getSessionId(); PropertyKeyBuilder keyBuilder = new PropertyKeyBuilder(clusterName); // check manager#hanlders Assert.assertEquals( participantToExpire.getHandlers().size(), 3, "Should have 3 handlers: CURRENTSTATE/{sessionId}, CONTROLLER, and MESSAGES"); // check zkclient#listeners Map<String, Set<IZkDataListener>> dataListeners = ZkTestHelper.getZkDataListener(participantToExpire.getZkClient()); Map<String, Set<IZkChildListener>> childListeners = ZkTestHelper.getZkChildListener(participantToExpire.getZkClient()); // printZkListeners(participantToExpire.getZkClient()); Assert.assertEquals( dataListeners.size(), 1, "Should have 1 path (CURRENTSTATE/{sessionId}/TestDB0) which has 1 data-listeners"); String path = keyBuilder .currentState(participantToExpire.getInstanceName(), oldSessionId, "TestDB0") .getPath(); Assert.assertEquals( dataListeners.get(path).size(), 1, "Should have 1 data-listeners on path: " + path); Assert.assertEquals( childListeners.size(), 3, "Should have 3 paths (CURRENTSTATE/{sessionId}, CONTROLLER, and MESSAGES) each of which has 1 child-listener"); path = keyBuilder.currentStates(participantToExpire.getInstanceName(), oldSessionId).getPath(); Assert.assertEquals( childListeners.get(path).size(), 1, "Should have 1 child-listener on path: " + path); path = keyBuilder.messages(participantToExpire.getInstanceName()).getPath(); Assert.assertEquals( childListeners.get(path).size(), 1, "Should have 1 child-listener on path: " + path); path = keyBuilder.controller().getPath(); Assert.assertEquals( childListeners.get(path).size(), 1, "Should have 1 child-listener on path: " + path); // check zookeeper#watches on client side Map<String, List<String>> watchPaths = ZkTestHelper.getZkWatch(participantToExpire.getZkClient()); // System.out.println("localhost_12918 zk-client side watchPaths: " + watchPaths + "\n"); Assert.assertEquals( watchPaths.get("dataWatches").size(), 4, "Should have 4 data-watches: CURRENTSTATE/{sessionId}, CURRENTSTATE/{sessionId}/TestDB, CONTROLLER, MESSAGES"); Assert.assertEquals( watchPaths.get("childWatches").size(), 3, "Should have 3 child-watches: CONTROLLER, MESSAGES, and CURRENTSTATE/{sessionId}"); // expire localhost_12918 System.out.println( "Expire participant: " + participantToExpire.getInstanceName() + ", session: " + participantToExpire.getSessionId()); ZkTestHelper.expireSession(participantToExpire.getZkClient()); String newSessionId = participantToExpire.getSessionId(); System.out.println( participantToExpire.getInstanceName() + " oldSessionId: " + oldSessionId + ", newSessionId: " + newSessionId); result = ClusterStateVerifier.verifyByZkCallback( new ClusterStateVerifier.BestPossAndExtViewZkVerifier(zkAddr, clusterName)); Assert.assertTrue(result); // check manager#hanlders Assert.assertEquals( participantToExpire.getHandlers().size(), 2, "Should have 2 handlers: CONTROLLER and MESSAGES. CURRENTSTATE/{sessionId} handler should be removed by CallbackHandler#handleChildChange()"); // check zkclient#listeners dataListeners = ZkTestHelper.getZkDataListener(participantToExpire.getZkClient()); childListeners = ZkTestHelper.getZkChildListener(participantToExpire.getZkClient()); // printZkListeners(participantToExpire.getZkClient()); Assert.assertTrue(dataListeners.isEmpty(), "Should have no data-listeners"); Assert.assertEquals( childListeners.size(), 3, "Should have 3 paths (CURRENTSTATE/{oldSessionId}, CONTROLLER, and MESSAGES). " + "CONTROLLER and MESSAGE has 1 child-listener each. CURRENTSTATE/{oldSessionId} doesn't have listener (ZkClient doesn't remove empty childListener set. probably a ZkClient bug. see ZkClient#unsubscribeChildChange())"); path = keyBuilder.currentStates(participantToExpire.getInstanceName(), oldSessionId).getPath(); Assert.assertEquals( childListeners.get(path).size(), 0, "Should have no child-listener on path: " + path); path = keyBuilder.messages(participantToExpire.getInstanceName()).getPath(); Assert.assertEquals( childListeners.get(path).size(), 1, "Should have 1 child-listener on path: " + path); path = keyBuilder.controller().getPath(); Assert.assertEquals( childListeners.get(path).size(), 1, "Should have 1 child-listener on path: " + path); // check zookeeper#watches on client side watchPaths = ZkTestHelper.getZkWatch(participantToExpire.getZkClient()); // System.out.println("localhost_12918 zk-client side watchPaths: " + watchPaths + "\n"); Assert.assertEquals( watchPaths.get("dataWatches").size(), 2, "Should have 2 data-watches: CONTROLLER and MESSAGES"); Assert.assertEquals( watchPaths.get("childWatches").size(), 2, "Should have 2 child-watches: CONTROLLER and MESSAGES"); Assert.assertEquals( watchPaths.get("existWatches").size(), 2, "Should have 2 exist-watches: CURRENTSTATE/{oldSessionId} and CURRENTSTATE/{oldSessionId}/TestDB0"); // another session expiry on localhost_12918 should clear the two exist-watches on // CURRENTSTATE/{oldSessionId} System.out.println( "Expire participant: " + participantToExpire.getInstanceName() + ", session: " + participantToExpire.getSessionId()); ZkTestHelper.expireSession(participantToExpire.getZkClient()); result = ClusterStateVerifier.verifyByZkCallback( new ClusterStateVerifier.BestPossAndExtViewZkVerifier(zkAddr, clusterName)); Assert.assertTrue(result); // check zookeeper#watches on client side watchPaths = ZkTestHelper.getZkWatch(participantToExpire.getZkClient()); // System.out.println("localhost_12918 zk-client side watchPaths: " + watchPaths + "\n"); Assert.assertEquals( watchPaths.get("dataWatches").size(), 2, "Should have 2 data-watches: CONTROLLER and MESSAGES"); Assert.assertEquals( watchPaths.get("childWatches").size(), 2, "Should have 2 child-watches: CONTROLLER and MESSAGES"); Assert.assertEquals( watchPaths.get("existWatches").size(), 0, "Should have no exist-watches. exist-watches on CURRENTSTATE/{oldSessionId} and CURRENTSTATE/{oldSessionId}/TestDB0 should be cleared during handleNewSession"); // Thread.sleep(1000); // clean up controller.syncStop(); for (int i = 0; i < n; i++) { participants[i].syncStop(); } System.out.println("END " + clusterName + " at " + new Date(System.currentTimeMillis())); }