/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.twitter.distributedlog.impl; import com.google.common.collect.Sets; import com.twitter.distributedlog.DistributedLogConfiguration; import com.twitter.distributedlog.TestDistributedLogBase; import com.twitter.distributedlog.TestZooKeeperClientBuilder; import com.twitter.distributedlog.ZooKeeperClient; import com.twitter.distributedlog.ZooKeeperClientUtils; import com.twitter.distributedlog.callback.NamespaceListener; import com.twitter.distributedlog.util.DLUtils; import com.twitter.distributedlog.util.OrderedScheduler; import com.twitter.distributedlog.util.Utils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import java.net.URI; import java.util.Iterator; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.*; /** * Test ZK Namespace Watcher. */ public class TestZKNamespaceWatcher extends TestDistributedLogBase { private final static int zkSessionTimeoutMs = 2000; @Rule public TestName runtime = new TestName(); protected final DistributedLogConfiguration baseConf = new DistributedLogConfiguration(); protected ZooKeeperClient zkc; protected OrderedScheduler scheduler; @Before public void setup() throws Exception { zkc = TestZooKeeperClientBuilder.newBuilder() .uri(createDLMURI("/")) .sessionTimeoutMs(zkSessionTimeoutMs) .build(); scheduler = OrderedScheduler.newBuilder() .name("test-zk-namespace-watcher") .corePoolSize(1) .build(); } @After public void teardown() throws Exception { if (null != zkc) { zkc.close(); } if (null != scheduler) { scheduler.shutdown(); } } private void createLogInNamespace(URI uri, String logName) throws Exception { String logPath = uri.getPath() + "/" + logName; Utils.zkCreateFullPathOptimistic(zkc, logPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } private void deleteLogInNamespace(URI uri, String logName) throws Exception { String logPath = uri.getPath() + "/" + logName; zkc.get().delete(logPath, -1); } @Test(timeout = 60000) public void testNamespaceListener() throws Exception { URI uri = createDLMURI("/" + runtime.getMethodName()); zkc.get().create(uri.getPath(), new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); DistributedLogConfiguration conf = new DistributedLogConfiguration(); conf.addConfiguration(baseConf); ZKNamespaceWatcher watcher = new ZKNamespaceWatcher(conf, uri, zkc, scheduler); final CountDownLatch[] latches = new CountDownLatch[10]; for (int i = 0; i < 10; i++) { latches[i] = new CountDownLatch(1); } final AtomicInteger numUpdates = new AtomicInteger(0); final AtomicReference<Set<String>> receivedLogs = new AtomicReference<Set<String>>(null); watcher.registerListener(new NamespaceListener() { @Override public void onStreamsChanged(Iterator<String> streams) { Set<String> streamSet = Sets.newHashSet(streams); int updates = numUpdates.incrementAndGet(); receivedLogs.set(streamSet); latches[updates - 1].countDown(); } }); // first update final Set<String> expectedLogs = Sets.newHashSet(); latches[0].await(); validateReceivedLogs(expectedLogs, receivedLogs.get()); // create test1 expectedLogs.add("test1"); createLogInNamespace(uri, "test1"); latches[1].await(); validateReceivedLogs(expectedLogs, receivedLogs.get()); // create invalid log createLogInNamespace(uri, ".test1"); latches[2].await(); validateReceivedLogs(expectedLogs, receivedLogs.get()); // create test2 expectedLogs.add("test2"); createLogInNamespace(uri, "test2"); latches[3].await(); validateReceivedLogs(expectedLogs, receivedLogs.get()); // delete test1 expectedLogs.remove("test1"); deleteLogInNamespace(uri, "test1"); latches[4].await(); validateReceivedLogs(expectedLogs, receivedLogs.get()); } private void validateReceivedLogs(Set<String> expectedLogs, Set<String> receivedLogs) { assertTrue(Sets.difference(expectedLogs, receivedLogs).isEmpty()); } @Test(timeout = 60000) public void testSessionExpired() throws Exception { URI uri = createDLMURI("/" + runtime.getMethodName()); zkc.get().create(uri.getPath(), new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); DistributedLogConfiguration conf = new DistributedLogConfiguration(); conf.addConfiguration(baseConf); ZKNamespaceWatcher watcher = new ZKNamespaceWatcher(conf, uri, zkc, scheduler); final CountDownLatch[] latches = new CountDownLatch[10]; for (int i = 0; i < 10; i++) { latches[i] = new CountDownLatch(1); } final AtomicInteger numUpdates = new AtomicInteger(0); final AtomicReference<Set<String>> receivedLogs = new AtomicReference<Set<String>>(null); watcher.registerListener(new NamespaceListener() { @Override public void onStreamsChanged(Iterator<String> streams) { Set<String> streamSet = Sets.newHashSet(streams); int updates = numUpdates.incrementAndGet(); receivedLogs.set(streamSet); latches[updates - 1].countDown(); } }); latches[0].await(); createLogInNamespace(uri, "test1"); latches[1].await(); createLogInNamespace(uri, "test2"); latches[2].await(); assertEquals(2, receivedLogs.get().size()); ZooKeeperClientUtils.expireSession(zkc, DLUtils.getZKServersFromDLUri(uri), zkSessionTimeoutMs); latches[3].await(); assertEquals(2, receivedLogs.get().size()); createLogInNamespace(uri, "test3"); latches[4].await(); assertEquals(3, receivedLogs.get().size()); } }