Package com.cloudera.flume.master

Source Code of com.cloudera.flume.master.TestZKBackedConfigStore$ZKThread

/**
* Licensed to Cloudera, Inc. under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  Cloudera, Inc. 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.cloudera.flume.master;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;
import org.junit.Ignore;
import org.junit.Test;

import com.cloudera.flume.agent.LogicalNode;
import com.cloudera.flume.conf.FlumeConfigData;
import com.cloudera.flume.conf.FlumeConfiguration;
import com.cloudera.util.Clock;
import com.cloudera.util.FileUtil;
import com.cloudera.util.Pair;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;

public class TestZKBackedConfigStore {
  protected static final Logger LOG = LoggerFactory.getLogger(TestZKBackedConfigStore.class);

  /**
   * Test that set and get work correctly, and that recovery after restart works
   * correctly.
   */
  @Test
  public void testZKBackedConfigStore() throws IOException,
      InterruptedException {
    for (int i = 0; i < 10; ++i) {
      LOG.info("Opening ZK, attempt " + i);
      File tmp = FileUtil.mktempdir();
      FlumeConfiguration cfg = FlumeConfiguration.createTestableConfiguration();
      cfg.set(FlumeConfiguration.MASTER_ZK_LOGDIR, tmp.getAbsolutePath());
      cfg.set(FlumeConfiguration.MASTER_ZK_SERVERS, "localhost:2181:3181:4181");
      ZooKeeperService.getAndInit(cfg);

      ZooKeeperConfigStore store = new ZooKeeperConfigStore();
      store.init();
      ConfigManager manager = new ConfigManager(store);
      manager.setConfig("foo", "my-test-flow", "null", "console");
      FlumeConfigData data = manager.getConfig("foo");
      assertEquals(data.getSinkConfig(), "console");
      assertEquals(data.getSourceConfig(), "null");
      store.shutdown();

      store = new ZooKeeperConfigStore();
      store.init();
      manager = new ConfigManager(store);
      data = manager.getConfig("foo");
      assertEquals(data.getSinkConfig(), "console");
      assertEquals(data.getSourceConfig(), "null");

      Map<String, FlumeConfigData> cfgs = new HashMap<String, FlumeConfigData>();
      String defaultFlowName = cfg.getDefaultFlowName();
      cfgs.put("bulk1", new FlumeConfigData(0, "s1", "sk1",
          LogicalNode.VERSION_INFIMUM, LogicalNode.VERSION_INFIMUM,
          "my-test-flow"));
      cfgs.put("bulk2", new FlumeConfigData(0, "s2", "sk2",
          LogicalNode.VERSION_INFIMUM, LogicalNode.VERSION_INFIMUM,
          defaultFlowName));
      store.bulkSetConfig(cfgs);

      data = manager.getConfig("bulk1");
      assertEquals(data.getSinkConfig(), "sk1");
      assertEquals(data.getSourceConfig(), "s1");
      assertEquals(data.getFlowID(), "my-test-flow");

      data = manager.getConfig("bulk2");
      assertEquals(data.getSinkConfig(), "sk2");
      assertEquals(data.getSourceConfig(), "s2");

      cfgs.put("bulk1", new FlumeConfigData(0, "s3", "sk3",
          LogicalNode.VERSION_INFIMUM, LogicalNode.VERSION_INFIMUM,
          defaultFlowName));
      cfgs.remove("bulk2");
      store.bulkSetConfig(cfgs);

      // Check that unchanged configs persist
      data = manager.getConfig("bulk2");
      assertEquals(data.getSinkConfig(), "sk2");
      assertEquals(data.getSourceConfig(), "s2");
      assertEquals(data.getFlowID(), defaultFlowName);
      store.shutdown();

      ZooKeeperService.get().shutdown();
      FileUtil.rmr(tmp);
    }
  }

  /**
   * Test that set and get work correctly, and that recovery after restart works
   * correctly.
   *
   * TODO add mechanism to close a ZooKeeperConfigStore to release resources.
   * (picking different ports right now to make test pass)
   */
  @Test
  public void testZKBackedConfigStoreNodes() throws IOException,
      InterruptedException {
    File tmp = FileUtil.mktempdir();
    FlumeConfiguration cfg = FlumeConfiguration.createTestableConfiguration();
    cfg.set(FlumeConfiguration.MASTER_ZK_LOGDIR, tmp.getAbsolutePath());
    cfg.set(FlumeConfiguration.MASTER_ZK_SERVERS, "localhost:2181:3181:4181");
    ZooKeeperService.getAndInit(cfg);

    ZooKeeperConfigStore store = new ZooKeeperConfigStore();
    store.init();

    ConfigManager manager = new ConfigManager(store);
    manager.addLogicalNode("physical", "logical1");
    manager.addLogicalNode("physical", "logical2");
    manager.addLogicalNode("physical", "logical3");
    manager.addLogicalNode("p2", "l2");
    manager.addLogicalNode("p3", "l3");

    List<String> lns = manager.getLogicalNode("physical");
    assertTrue(lns.contains("logical1"));
    assertTrue(lns.contains("logical2"));
    assertTrue(lns.contains("logical3"));

    assertTrue(manager.getLogicalNode("p2").contains("l2"));
    assertTrue(manager.getLogicalNode("p3").contains("l3"));
    store.shutdown();

    store = new ZooKeeperConfigStore();
    store.init();
    manager = new ConfigManager(store);

    lns = manager.getLogicalNode("physical");
    assertTrue(lns.contains("logical1"));
    assertTrue(lns.contains("logical2"));
    assertTrue(lns.contains("logical3"));

    assertTrue(manager.getLogicalNode("p2").contains("l2"));
    assertTrue(manager.getLogicalNode("p3").contains("l3"));
    store.shutdown();

    ZooKeeperService.get().shutdown();

    FileUtil.rmr(tmp);
  }

  /**
   * Test that watches are fired correctly for logical nodes
   */
  @Test
  public void testZBCSLogicalWatches() throws IOException, InterruptedException {
    FlumeConfiguration cfg = FlumeConfiguration.createTestableConfiguration();
    cfg.set(FlumeConfiguration.MASTER_ZK_SERVERS, "localhost:2181:3181:4181");
    File tmp = FileUtil.mktempdir();
    cfg.set(FlumeConfiguration.MASTER_ZK_LOGDIR, tmp.getAbsolutePath());
    cfg.setBoolean(FlumeConfiguration.MASTER_ZK_USE_EXTERNAL, false);
    ZooKeeperService.getAndInit(cfg);

    ZooKeeperConfigStore store = new ZooKeeperConfigStore();
    store.init();
    ZooKeeperConfigStore store2 = new ZooKeeperConfigStore();
    store2.init();
    ConfigManager manager1 = new ConfigManager(store);
    ConfigManager manager2 = new ConfigManager(store2);
    manager1.addLogicalNode("logical-watch", "logical1");

    // There is no convenient way to avoid this sleep
    Thread.sleep(2000);

    // Check that the watch has happened and that the new value
    // will be correctly read
    assertEquals("logical1", manager2.getLogicalNode("logical-watch").get(0));
    store.shutdown();
    store2.shutdown();
    ZooKeeperService.get().shutdown();
    FileUtil.rmr(tmp);
  }

  /**
   * Test that watches are fired correctly for ChokeMap.
   */
  @Test
  public void testZBCSChokeWatches() throws IOException, InterruptedException {
    FlumeConfiguration cfg = FlumeConfiguration.createTestableConfiguration();
    cfg.set(FlumeConfiguration.MASTER_ZK_SERVERS, "localhost:2181:3181:4181");
    File tmp = FileUtil.mktempdir();
    cfg.set(FlumeConfiguration.MASTER_ZK_LOGDIR, tmp.getAbsolutePath());
    cfg.setBoolean(FlumeConfiguration.MASTER_ZK_USE_EXTERNAL, false);
    ZooKeeperService.getAndInit(cfg);

    ZooKeeperConfigStore store = new ZooKeeperConfigStore();
    store.init();
    ZooKeeperConfigStore store2 = new ZooKeeperConfigStore();
    store2.init();

    store.addChokeLimit("vb", "a", 1000);
    // There is no convenient way to avoid this sleep
    Thread.sleep(2000);

    // Check that the watch has happened and that the new value
    // will be correctly read
    assertEquals(1000, (int) (store2.getChokeMap("vb").get("a")));
    store.shutdown();
    store2.shutdown();
    ZooKeeperService.get().shutdown();
    FileUtil.rmr(tmp);
  }

  /**
   * Test disconnection
   */
  @Test
  public void testZBCSLoseZKCnxn() throws IOException, InterruptedException {
    File tmp = FileUtil.mktempdir();
    FlumeConfiguration cfg = FlumeConfiguration.createTestableConfiguration();
    cfg.set(FlumeConfiguration.MASTER_ZK_LOGDIR, tmp.getAbsolutePath());
    cfg.set(FlumeConfiguration.MASTER_ZK_SERVERS, "localhost:2181:3181:4181");
    ZooKeeperService.getAndInit(cfg);

    ZooKeeperConfigStore store = new ZooKeeperConfigStore();
    store.init();
    store.setConfig("foo", "my-test-flow", "fab", "fat");

    ZooKeeperService.get().shutdown();
    Thread.sleep(30 * 1000);
    IOException ex = null;

    try {
      store.setConfig("foo", cfg.getDefaultFlowName(), "bar", "baz");
    } catch (IOException e) {
      ex = e;
    }
    assertNotNull("Expected IOException not thrown - still connected to ZK?",
        ex);
    store.shutdown();
    FileUtil.rmr(tmp);
  }

  final CountDownLatch latch = new CountDownLatch(3);

  /**
   * Initialises a single server of a ZK ensemble. Must be threaded so that all
   * servers can come up at once.
   */
  protected class ZKThread extends Thread {
    protected final int serverid;
    final File tmp;
    final FlumeConfiguration cfg = FlumeConfiguration
        .createTestableConfiguration();
    final ZooKeeperService zkService = new ZooKeeperService();

    public ZKThread(int serverid) throws IOException {
      super("ZKThread-" + serverid);
      this.serverid = serverid;
      tmp = FileUtil.mktempdir();
    }

    @Override
    public void run() {
      cfg
          .set(FlumeConfiguration.MASTER_ZK_SERVERS,
              "localhost:2181:3181:4181,localhost:2182:3182:4182,localhost:2183:3183:4183");
      cfg.set(FlumeConfiguration.MASTER_SERVERS,
          "localhost,localhost,localhost");
      cfg.set(FlumeConfiguration.MASTER_ZK_LOGDIR, tmp.getAbsolutePath());

      cfg.setInt(FlumeConfiguration.MASTER_SERVER_ID, serverid);
      try {
        zkService.init(cfg);
      } catch (Exception e) {
        LOG.error("Exception when starting ZK " + serverid, e);

        // Not counting down the latch will cause the calling test to timeout
        return;
      }
      latch.countDown();
    }

    public ZooKeeperService getService() {
      return zkService;
    }

    public void shutdown() throws IOException {
      zkService.shutdown();
      FileUtil.rmr(tmp);
    }
  }

  /**
   * Test good behaviour when servers fail.
   */
  @Test
  public void testEnsembleFailure() throws IOException, InterruptedException {
    ZKThread zk1 = new ZKThread(0);
    ZKThread zk2 = new ZKThread(1);
    ZKThread zk3 = new ZKThread(2);

    zk1.start();
    zk2.start();
    zk3.start();

    if (!latch.await(10, TimeUnit.SECONDS)) {
      fail("ZooKeeper did not come up!");
    }

    ZooKeeperConfigStore store = new ZooKeeperConfigStore(zk1.getService());
    store.init();

    String defaultFlowName = FlumeConfiguration.get().getDefaultFlowName();
    store.setConfig("foo", defaultFlowName, "null", "baz");

    zk1.shutdown();

    store.setConfig("foo2", defaultFlowName, "baz", "bar");

    zk2.shutdown();
    zk3.shutdown();
    store.shutdown();
  }

  /**
   * Test to make sure unmapping logical nodes from physical nodes and survives
   * a zk restart.
   */
  @Test
  public void testUnmapAllNodes() throws IOException, InterruptedException {

    File tmp = FileUtil.mktempdir();
    FlumeConfiguration cfg = FlumeConfiguration.createTestableConfiguration();
    cfg.set(FlumeConfiguration.MASTER_ZK_LOGDIR, tmp.getAbsolutePath());
    cfg.set(FlumeConfiguration.MASTER_ZK_SERVERS, "localhost:2181:3181:4181");
    cfg.setInt(FlumeConfiguration.MASTER_SERVER_ID, 0);
    ZooKeeperService.getAndInit(cfg);
    ZooKeeperConfigStore store = new ZooKeeperConfigStore();
    store.init();

    ConfigManager manager = new ConfigManager(store);
    manager.addLogicalNode("physical", "logical1");
    manager.addLogicalNode("physical", "logical2");
    manager.addLogicalNode("physical", "logical3");
    manager.addLogicalNode("p2", "l2");
    manager.addLogicalNode("p3", "l3");

    manager.unmapAllLogicalNodes();

    List<String> lns = manager.getLogicalNode("physical");
    assertFalse(lns.contains("logical1"));
    assertFalse(lns.contains("logical2"));
    assertFalse(lns.contains("logical3"));

    assertFalse(manager.getLogicalNode("p2").contains("l2"));
    assertFalse(manager.getLogicalNode("p3").contains("l3"));

    store.shutdown();
    store = new ZooKeeperConfigStore();
    store.init();
    manager = new ConfigManager(store);

    lns = manager.getLogicalNode("physical");
    assertFalse(lns.contains("logical1"));
    assertFalse(lns.contains("logical2"));
    assertFalse(lns.contains("logical3"));

    assertFalse(manager.getLogicalNode("p2").contains("l2"));
    assertFalse(manager.getLogicalNode("p3").contains("l3"));
    store.shutdown();
    ZooKeeperService.get().shutdown();
    FileUtil.rmr(tmp);
  }

  /**
   * Test that the version is correctly incremented
   */
  @Test
  public void testVersionIncrement() throws IOException, InterruptedException,
      KeeperException {
    File tmp = FileUtil.mktempdir();
    FlumeConfiguration cfg = FlumeConfiguration.createTestableConfiguration();
    cfg.set(FlumeConfiguration.MASTER_ZK_LOGDIR, tmp.getAbsolutePath());
    cfg.set(FlumeConfiguration.MASTER_ZK_SERVERS, "localhost:2181:3181:4181");
    cfg.setInt(FlumeConfiguration.MASTER_SERVER_ID, 0);

    ZooKeeperService zk = new ZooKeeperService();
    zk.init(cfg);

    ZooKeeperConfigStore store = new ZooKeeperConfigStore(zk);
    store.init();
    String defaultFlowName = cfg.getDefaultFlowName();
    store.setConfig("foo", defaultFlowName, "null", "baz");
    store.setConfig("foo2", defaultFlowName, "null", "baz");
    store.setConfig("foo3", defaultFlowName, "null", "baz");

    ZKClient client = zk.createClient();
    client.init();
    List<String> children = client.getChildren(ZooKeeperConfigStore.CFGS_PATH,
        false);
    assertEquals("Expected 3 configs", 3, children.size());

    // Note children not necessarily returned in creation order
    Collections.sort(children);

    assertEquals("Expected config to be numbered 0", 0L, ZKClient
        .extractSuffix("cfg-", children.get(0)));
    assertEquals("Expected config to be numbered 1", 1L, ZKClient
        .extractSuffix("cfg-", children.get(1)));
    assertEquals("Expected config to be numbered 2", 2L, ZKClient
        .extractSuffix("cfg-", children.get(2)));
    store.shutdown();
    client.close();
    zk.shutdown();
    FileUtil.rmr(tmp);
  }

  /**
   * This test creates a zkcs and then hijacks its session through another
   * client. Then we try to use the zkcs to make sure that it's reconnected
   * correctly.
   */
  @Test
  @Ignore("Timing issue prevents this succeeding on Hudson")
  public void testLostSessionOK() throws IOException, InterruptedException,
      KeeperException {
    File tmp = FileUtil.mktempdir();
    FlumeConfiguration cfg = FlumeConfiguration.createTestableConfiguration();
    cfg.set(FlumeConfiguration.MASTER_ZK_LOGDIR, tmp.getAbsolutePath());
    cfg.set(FlumeConfiguration.MASTER_ZK_SERVERS, "localhost:2181:3181:4181");
    cfg.setInt(FlumeConfiguration.MASTER_SERVER_ID, 0);

    ZooKeeperService zk = new ZooKeeperService();
    zk.init(cfg);

    ZooKeeperConfigStore store = new ZooKeeperConfigStore(zk);
    store.init();
    String defaultFlowName = cfg.getDefaultFlowName();
    store.setConfig("foo", defaultFlowName, "bar", "baz");
    long sessionid = store.client.zk.getSessionId();
    byte[] sessionpass = store.client.zk.getSessionPasswd();

    // Force session expiration
    Watcher watcher = new Watcher() {

      @Override
      public void process(WatchedEvent event) {
      }

    };
    ZooKeeper zkClient = new ZooKeeper("localhost:2181", 1000, watcher,
        sessionid, sessionpass);
    zkClient.close();

    ZKClient updatingClient = new ZKClient("localhost:2181");
    updatingClient.init();
    Stat stat = new Stat();

    store.client.getChildren("/flume-cfgs", false);

    byte[] bytes = updatingClient.getData("/flume-cfgs/cfg-0000000000", false,
        stat);

    String badCfg = new String(bytes)
        + "\n1,1,default-flow@@bur : null | null;";

    // Force a cfg into ZK to be reloaded by the (hopefully functioning) store
    updatingClient.create("/flume-cfgs/cfg-", badCfg.getBytes(),
        Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);

    assertTrue(store.client.zk.getSessionId() != sessionid);

    // This sleep is ugly, but we have to wait for the watch to fire
    Clock.sleep(2000);

    assertEquals("null", store.getConfig("bur").getSinkConfig());
  }

  /**
   * Test that Avro-based serialization of phys->logical node maps works
   */
  @Test
  public void testSerializeNodeMap() throws IOException {
    ListMultimap<String, String> nodeMap = ArrayListMultimap
        .<String, String> create();
    nodeMap.put("foo", "bar");
    nodeMap.put("foo", "baz");
    nodeMap.put("foz", "bat");
    byte[] serialized = ZooKeeperConfigStore.serializeNodeMap(nodeMap);
    List<Pair<String, List<String>>> ret = ZooKeeperConfigStore
        .deserializeNodeMap(serialized);

    ListMultimap<String, String> outMap = ArrayListMultimap
        .<String, String> create();
    for (Pair<String, List<String>> p : ret) {
      outMap.putAll(p.getLeft(), p.getRight());
    }
    assertEquals(nodeMap, outMap);
  }

  /**
   * Test that Avro-based serialization of node configs works
   */
  @Test
  public void testSerializeConfigs() throws IOException {
    Map<String, FlumeConfigData> cfgmap = new HashMap<String, FlumeConfigData>();
    FlumeConfigData fcd = new FlumeConfigData();
    fcd.flowID = "my-flow";
    fcd.sinkConfig = "my-sink";
    fcd.sourceConfig = "my-source";
    fcd.timestamp = 10L;
    fcd.sinkVersion = 10;
    fcd.sourceVersion = 100;
    byte[] serialized = ZooKeeperConfigStore.serializeConfigs(cfgmap);

    Map<String, FlumeConfigData> outmap = ZooKeeperConfigStore
        .deserializeConfigs(serialized);

    assertEquals(cfgmap, outmap);
  }

  /**
   * Test that Avro-based serialization of chokemap works
   */
  @Test
  public void testSerializeChokeMap() throws IOException {
    Map<String, Map<String, Integer>> chokeMap = new HashMap<String, Map<String, Integer>>();

    // add bunch of ChokeIds in the map, serialize and then de-serialize it and
    // make sure the returned chokemap is the same.
    Map<String, Integer> tempMapA = new HashMap<String, Integer>();
    Map<String, Integer> tempMapB = new HashMap<String, Integer>();

    tempMapA.put("A", 1000);
    tempMapA.put("Z", 1000);

    tempMapB.put("B", 1000);

    chokeMap.put("foo1", tempMapA);
    chokeMap.put("foo2", tempMapB);

    byte[] serialized = ZooKeeperConfigStore.serializeChokeMap(chokeMap);
    Map<String, Map<String, Integer>> ret = ZooKeeperConfigStore
        .deserializeChokeMap(serialized);

    assertEquals(chokeMap, ret);
  }
}
TOP

Related Classes of com.cloudera.flume.master.TestZKBackedConfigStore$ZKThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.