/** * Copyright The Apache Software Foundation * * 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 org.apache.hadoop.hbase.zookeeper; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.Abortable; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.testclassification.MediumTests; import org.apache.hadoop.hbase.testclassification.MiscTests; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; import org.apache.zookeeper.ZooDefs.Ids; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; /** * Test ZooKeeper multi-update functionality */ @Category({MiscTests.class, MediumTests.class}) public class TestZKMulti { private static final Log LOG = LogFactory.getLog(TestZKMulti.class); private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); private static ZooKeeperWatcher zkw = null; @BeforeClass public static void setUpBeforeClass() throws Exception { TEST_UTIL.startMiniZKCluster(); Configuration conf = TEST_UTIL.getConfiguration(); Abortable abortable = new Abortable() { @Override public void abort(String why, Throwable e) { LOG.info(why, e); } @Override public boolean isAborted() { return false; } }; zkw = new ZooKeeperWatcher(conf, "TestZKMulti", abortable, true); } @AfterClass public static void tearDownAfterClass() throws Exception { TEST_UTIL.shutdownMiniZKCluster(); } @Test (timeout=60000) public void testSimpleMulti() throws Exception { // null multi ZKUtil.multiOrSequential(zkw, null, false); // empty multi ZKUtil.multiOrSequential(zkw, new LinkedList<>(), false); // single create String path = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testSimpleMulti"); LinkedList<ZKUtilOp> singleCreate = new LinkedList<>(); singleCreate.add(ZKUtilOp.createAndFailSilent(path, new byte[0])); ZKUtil.multiOrSequential(zkw, singleCreate, false); assertTrue(ZKUtil.checkExists(zkw, path) != -1); // single setdata LinkedList<ZKUtilOp> singleSetData = new LinkedList<>(); byte [] data = Bytes.toBytes("foobar"); singleSetData.add(ZKUtilOp.setData(path, data)); ZKUtil.multiOrSequential(zkw, singleSetData, false); assertTrue(Bytes.equals(ZKUtil.getData(zkw, path), data)); // single delete LinkedList<ZKUtilOp> singleDelete = new LinkedList<>(); singleDelete.add(ZKUtilOp.deleteNodeFailSilent(path)); ZKUtil.multiOrSequential(zkw, singleDelete, false); assertTrue(ZKUtil.checkExists(zkw, path) == -1); } @Test (timeout=60000) public void testComplexMulti() throws Exception { String path1 = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testComplexMulti1"); String path2 = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testComplexMulti2"); String path3 = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testComplexMulti3"); String path4 = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testComplexMulti4"); String path5 = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testComplexMulti5"); String path6 = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testComplexMulti6"); // create 4 nodes that we'll setData on or delete later LinkedList<ZKUtilOp> create4Nodes = new LinkedList<>(); create4Nodes.add(ZKUtilOp.createAndFailSilent(path1, Bytes.toBytes(path1))); create4Nodes.add(ZKUtilOp.createAndFailSilent(path2, Bytes.toBytes(path2))); create4Nodes.add(ZKUtilOp.createAndFailSilent(path3, Bytes.toBytes(path3))); create4Nodes.add(ZKUtilOp.createAndFailSilent(path4, Bytes.toBytes(path4))); ZKUtil.multiOrSequential(zkw, create4Nodes, false); assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1), Bytes.toBytes(path1))); assertTrue(Bytes.equals(ZKUtil.getData(zkw, path2), Bytes.toBytes(path2))); assertTrue(Bytes.equals(ZKUtil.getData(zkw, path3), Bytes.toBytes(path3))); assertTrue(Bytes.equals(ZKUtil.getData(zkw, path4), Bytes.toBytes(path4))); // do multiple of each operation (setData, delete, create) LinkedList<ZKUtilOp> ops = new LinkedList<>(); // setData ops.add(ZKUtilOp.setData(path1, Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1)))); ops.add(ZKUtilOp.setData(path2, Bytes.add(Bytes.toBytes(path2), Bytes.toBytes(path2)))); // delete ops.add(ZKUtilOp.deleteNodeFailSilent(path3)); ops.add(ZKUtilOp.deleteNodeFailSilent(path4)); // create ops.add(ZKUtilOp.createAndFailSilent(path5, Bytes.toBytes(path5))); ops.add(ZKUtilOp.createAndFailSilent(path6, Bytes.toBytes(path6))); ZKUtil.multiOrSequential(zkw, ops, false); assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1), Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1)))); assertTrue(Bytes.equals(ZKUtil.getData(zkw, path2), Bytes.add(Bytes.toBytes(path2), Bytes.toBytes(path2)))); assertTrue(ZKUtil.checkExists(zkw, path3) == -1); assertTrue(ZKUtil.checkExists(zkw, path4) == -1); assertTrue(Bytes.equals(ZKUtil.getData(zkw, path5), Bytes.toBytes(path5))); assertTrue(Bytes.equals(ZKUtil.getData(zkw, path6), Bytes.toBytes(path6))); } @Test (timeout=60000) public void testSingleFailure() throws Exception { // try to delete a node that doesn't exist boolean caughtNoNode = false; String path = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testSingleFailureZ"); LinkedList<ZKUtilOp> ops = new LinkedList<>(); ops.add(ZKUtilOp.deleteNodeFailSilent(path)); try { ZKUtil.multiOrSequential(zkw, ops, false); } catch (KeeperException.NoNodeException nne) { caughtNoNode = true; } assertTrue(caughtNoNode); // try to setData on a node that doesn't exist caughtNoNode = false; ops = new LinkedList<>(); ops.add(ZKUtilOp.setData(path, Bytes.toBytes(path))); try { ZKUtil.multiOrSequential(zkw, ops, false); } catch (KeeperException.NoNodeException nne) { caughtNoNode = true; } assertTrue(caughtNoNode); // try to create on a node that already exists boolean caughtNodeExists = false; ops = new LinkedList<>(); ops.add(ZKUtilOp.createAndFailSilent(path, Bytes.toBytes(path))); ZKUtil.multiOrSequential(zkw, ops, false); try { ZKUtil.multiOrSequential(zkw, ops, false); } catch (KeeperException.NodeExistsException nee) { caughtNodeExists = true; } assertTrue(caughtNodeExists); } @Test (timeout=60000) public void testSingleFailureInMulti() throws Exception { // try a multi where all but one operation succeeds String pathA = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testSingleFailureInMultiA"); String pathB = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testSingleFailureInMultiB"); String pathC = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testSingleFailureInMultiC"); LinkedList<ZKUtilOp> ops = new LinkedList<>(); ops.add(ZKUtilOp.createAndFailSilent(pathA, Bytes.toBytes(pathA))); ops.add(ZKUtilOp.createAndFailSilent(pathB, Bytes.toBytes(pathB))); ops.add(ZKUtilOp.deleteNodeFailSilent(pathC)); boolean caughtNoNode = false; try { ZKUtil.multiOrSequential(zkw, ops, false); } catch (KeeperException.NoNodeException nne) { caughtNoNode = true; } assertTrue(caughtNoNode); // assert that none of the operations succeeded assertTrue(ZKUtil.checkExists(zkw, pathA) == -1); assertTrue(ZKUtil.checkExists(zkw, pathB) == -1); assertTrue(ZKUtil.checkExists(zkw, pathC) == -1); } @Test (timeout=60000) public void testMultiFailure() throws Exception { String pathX = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testMultiFailureX"); String pathY = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testMultiFailureY"); String pathZ = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testMultiFailureZ"); // create X that we will use to fail create later LinkedList<ZKUtilOp> ops = new LinkedList<>(); ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX))); ZKUtil.multiOrSequential(zkw, ops, false); // fail one of each create ,setData, delete String pathV = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testMultiFailureV"); String pathW = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "testMultiFailureW"); ops = new LinkedList<>(); ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX))); // fail -- already exists ops.add(ZKUtilOp.setData(pathY, Bytes.toBytes(pathY))); // fail -- doesn't exist ops.add(ZKUtilOp.deleteNodeFailSilent(pathZ)); // fail -- doesn't exist ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathV))); // pass ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathW))); // pass boolean caughtNodeExists = false; try { ZKUtil.multiOrSequential(zkw, ops, false); } catch (KeeperException.NodeExistsException nee) { // check first operation that fails throws exception caughtNodeExists = true; } assertTrue(caughtNodeExists); // check that no modifications were made assertFalse(ZKUtil.checkExists(zkw, pathX) == -1); assertTrue(ZKUtil.checkExists(zkw, pathY) == -1); assertTrue(ZKUtil.checkExists(zkw, pathZ) == -1); assertTrue(ZKUtil.checkExists(zkw, pathW) == -1); assertTrue(ZKUtil.checkExists(zkw, pathV) == -1); // test that with multiple failures, throws an exception corresponding to first failure in list ops = new LinkedList<>(); ops.add(ZKUtilOp.setData(pathY, Bytes.toBytes(pathY))); // fail -- doesn't exist ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX))); // fail -- exists boolean caughtNoNode = false; try { ZKUtil.multiOrSequential(zkw, ops, false); } catch (KeeperException.NoNodeException nne) { // check first operation that fails throws exception caughtNoNode = true; } assertTrue(caughtNoNode); // check that no modifications were made assertFalse(ZKUtil.checkExists(zkw, pathX) == -1); assertTrue(ZKUtil.checkExists(zkw, pathY) == -1); assertTrue(ZKUtil.checkExists(zkw, pathZ) == -1); assertTrue(ZKUtil.checkExists(zkw, pathW) == -1); assertTrue(ZKUtil.checkExists(zkw, pathV) == -1); } @Test (timeout=60000) public void testRunSequentialOnMultiFailure() throws Exception { String path1 = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "runSequential1"); String path2 = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "runSequential2"); String path3 = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "runSequential3"); String path4 = ZKUtil.joinZNode(zkw.znodePaths.baseZNode, "runSequential4"); // create some nodes that we will use later LinkedList<ZKUtilOp> ops = new LinkedList<>(); ops.add(ZKUtilOp.createAndFailSilent(path1, Bytes.toBytes(path1))); ops.add(ZKUtilOp.createAndFailSilent(path2, Bytes.toBytes(path2))); ZKUtil.multiOrSequential(zkw, ops, false); // test that, even with operations that fail, the ones that would pass will pass // with runSequentialOnMultiFailure ops = new LinkedList<>(); ops.add(ZKUtilOp.setData(path1, Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1)))); // pass ops.add(ZKUtilOp.deleteNodeFailSilent(path2)); // pass ops.add(ZKUtilOp.deleteNodeFailSilent(path3)); // fail -- node doesn't exist ops.add(ZKUtilOp.createAndFailSilent(path4, Bytes.add(Bytes.toBytes(path4), Bytes.toBytes(path4)))); // pass ZKUtil.multiOrSequential(zkw, ops, true); assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1), Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1)))); assertTrue(ZKUtil.checkExists(zkw, path2) == -1); assertTrue(ZKUtil.checkExists(zkw, path3) == -1); assertFalse(ZKUtil.checkExists(zkw, path4) == -1); } /** * Verifies that for the given root node, it should delete all the child nodes * recursively using multi-update api. */ @Test (timeout=60000) public void testdeleteChildrenRecursivelyMulti() throws Exception { String parentZNode = "/testRootMulti"; createZNodeTree(parentZNode); ZKUtil.deleteChildrenRecursivelyMultiOrSequential(zkw, true, parentZNode); assertTrue("Wrongly deleted parent znode!", ZKUtil.checkExists(zkw, parentZNode) > -1); List<String> children = zkw.getRecoverableZooKeeper().getChildren( parentZNode, false); assertTrue("Failed to delete child znodes!", 0 == children.size()); } /** * Verifies that for the given root node, it should delete all the nodes recursively using * multi-update api. */ @Test(timeout = 60000) public void testDeleteNodeRecursivelyMulti() throws Exception { String parentZNode = "/testdeleteNodeRecursivelyMulti"; createZNodeTree(parentZNode); ZKUtil.deleteNodeRecursively(zkw, parentZNode); assertTrue("Parent znode should be deleted.", ZKUtil.checkExists(zkw, parentZNode) == -1); } @Test(timeout = 60000) public void testDeleteNodeRecursivelyMultiOrSequential() throws Exception { String parentZNode1 = "/testdeleteNode1"; String parentZNode2 = "/testdeleteNode2"; String parentZNode3 = "/testdeleteNode3"; createZNodeTree(parentZNode1); createZNodeTree(parentZNode2); createZNodeTree(parentZNode3); ZKUtil.deleteNodeRecursivelyMultiOrSequential(zkw, false, parentZNode1, parentZNode2, parentZNode3); assertTrue("Parent znode 1 should be deleted.", ZKUtil.checkExists(zkw, parentZNode1) == -1); assertTrue("Parent znode 2 should be deleted.", ZKUtil.checkExists(zkw, parentZNode2) == -1); assertTrue("Parent znode 3 should be deleted.", ZKUtil.checkExists(zkw, parentZNode3) == -1); } @Test(timeout = 60000) public void testDeleteChildrenRecursivelyMultiOrSequential() throws Exception { String parentZNode1 = "/testdeleteChildren1"; String parentZNode2 = "/testdeleteChildren2"; String parentZNode3 = "/testdeleteChildren3"; createZNodeTree(parentZNode1); createZNodeTree(parentZNode2); createZNodeTree(parentZNode3); ZKUtil.deleteChildrenRecursivelyMultiOrSequential(zkw, true, parentZNode1, parentZNode2, parentZNode3); assertTrue("Wrongly deleted parent znode 1!", ZKUtil.checkExists(zkw, parentZNode1) > -1); List<String> children = zkw.getRecoverableZooKeeper().getChildren(parentZNode1, false); assertTrue("Failed to delete child znodes of parent znode 1!", 0 == children.size()); assertTrue("Wrongly deleted parent znode 2!", ZKUtil.checkExists(zkw, parentZNode2) > -1); children = zkw.getRecoverableZooKeeper().getChildren(parentZNode2, false); assertTrue("Failed to delete child znodes of parent znode 1!", 0 == children.size()); assertTrue("Wrongly deleted parent znode 3!", ZKUtil.checkExists(zkw, parentZNode3) > -1); children = zkw.getRecoverableZooKeeper().getChildren(parentZNode3, false); assertTrue("Failed to delete child znodes of parent znode 1!", 0 == children.size()); } private void createZNodeTree(String rootZNode) throws KeeperException, InterruptedException { List<Op> opList = new ArrayList<>(); opList.add(Op.create(rootZNode, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); int level = 0; String parentZNode = rootZNode; while (level < 10) { // define parent node parentZNode = parentZNode + "/" + level; opList.add(Op.create(parentZNode, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); int elements = 0; // add elements to the parent node while (elements < level) { opList.add(Op.create(parentZNode + "/" + elements, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); elements++; } level++; } zkw.getRecoverableZooKeeper().multi(opList); } }