001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.zookeeper;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertNotEquals;
022import static org.junit.Assert.assertTrue;
023
024import java.util.ArrayList;
025import java.util.LinkedList;
026import java.util.List;
027import org.apache.hadoop.conf.Configuration;
028import org.apache.hadoop.hbase.Abortable;
029import org.apache.hadoop.hbase.HBaseClassTestRule;
030import org.apache.hadoop.hbase.HBaseZKTestingUtility;
031import org.apache.hadoop.hbase.testclassification.MediumTests;
032import org.apache.hadoop.hbase.testclassification.ZKTests;
033import org.apache.hadoop.hbase.util.Bytes;
034import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp;
035import org.apache.zookeeper.CreateMode;
036import org.apache.zookeeper.KeeperException;
037import org.apache.zookeeper.Op;
038import org.apache.zookeeper.ZooDefs.Ids;
039import org.junit.AfterClass;
040import org.junit.BeforeClass;
041import org.junit.ClassRule;
042import org.junit.Test;
043import org.junit.experimental.categories.Category;
044import org.slf4j.Logger;
045import org.slf4j.LoggerFactory;
046
047/**
048 * Test ZooKeeper multi-update functionality.
049 */
050@Category({ ZKTests.class, MediumTests.class })
051public class TestZKMulti {
052  @ClassRule
053  public static final HBaseClassTestRule CLASS_RULE =
054      HBaseClassTestRule.forClass(TestZKMulti.class);
055
056  private static final Logger LOG = LoggerFactory.getLogger(TestZKMulti.class);
057  private final static HBaseZKTestingUtility TEST_UTIL = new HBaseZKTestingUtility();
058  private static ZKWatcher zkw = null;
059
060  @BeforeClass
061  public static void setUpBeforeClass() throws Exception {
062    TEST_UTIL.startMiniZKCluster();
063    Configuration conf = TEST_UTIL.getConfiguration();
064    Abortable abortable = new Abortable() {
065      @Override
066      public void abort(String why, Throwable e) {
067        LOG.info(why, e);
068      }
069
070      @Override
071      public boolean isAborted() {
072        return false;
073      }
074    };
075    zkw = new ZKWatcher(conf,
076      "TestZKMulti", abortable, true);
077  }
078
079  @AfterClass
080  public static void tearDownAfterClass() throws Exception {
081    TEST_UTIL.shutdownMiniZKCluster();
082  }
083
084  @Test
085  public void testSimpleMulti() throws Exception {
086    // null multi
087    ZKUtil.multiOrSequential(zkw, null, false);
088
089    // empty multi
090    ZKUtil.multiOrSequential(zkw, new LinkedList<>(), false);
091
092    // single create
093    String path = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testSimpleMulti");
094    LinkedList<ZKUtilOp> singleCreate = new LinkedList<>();
095    singleCreate.add(ZKUtilOp.createAndFailSilent(path, new byte[0]));
096    ZKUtil.multiOrSequential(zkw, singleCreate, false);
097    assertTrue(ZKUtil.checkExists(zkw, path) != -1);
098
099    // single setdata
100    LinkedList<ZKUtilOp> singleSetData = new LinkedList<>();
101    byte [] data = Bytes.toBytes("foobar");
102    singleSetData.add(ZKUtilOp.setData(path, data));
103    ZKUtil.multiOrSequential(zkw, singleSetData, false);
104    assertTrue(Bytes.equals(ZKUtil.getData(zkw, path), data));
105
106    // single delete
107    LinkedList<ZKUtilOp> singleDelete = new LinkedList<>();
108    singleDelete.add(ZKUtilOp.deleteNodeFailSilent(path));
109    ZKUtil.multiOrSequential(zkw, singleDelete, false);
110    assertEquals(-1, ZKUtil.checkExists(zkw, path));
111  }
112
113  @Test
114  public void testComplexMulti() throws Exception {
115    String path1 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti1");
116    String path2 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti2");
117    String path3 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti3");
118    String path4 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti4");
119    String path5 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti5");
120    String path6 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti6");
121    // create 4 nodes that we'll setData on or delete later
122    LinkedList<ZKUtilOp> create4Nodes = new LinkedList<>();
123    create4Nodes.add(ZKUtilOp.createAndFailSilent(path1, Bytes.toBytes(path1)));
124    create4Nodes.add(ZKUtilOp.createAndFailSilent(path2, Bytes.toBytes(path2)));
125    create4Nodes.add(ZKUtilOp.createAndFailSilent(path3, Bytes.toBytes(path3)));
126    create4Nodes.add(ZKUtilOp.createAndFailSilent(path4, Bytes.toBytes(path4)));
127    ZKUtil.multiOrSequential(zkw, create4Nodes, false);
128    assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1), Bytes.toBytes(path1)));
129    assertTrue(Bytes.equals(ZKUtil.getData(zkw, path2), Bytes.toBytes(path2)));
130    assertTrue(Bytes.equals(ZKUtil.getData(zkw, path3), Bytes.toBytes(path3)));
131    assertTrue(Bytes.equals(ZKUtil.getData(zkw, path4), Bytes.toBytes(path4)));
132
133    // do multiple of each operation (setData, delete, create)
134    LinkedList<ZKUtilOp> ops = new LinkedList<>();
135    // setData
136    ops.add(ZKUtilOp.setData(path1, Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1))));
137    ops.add(ZKUtilOp.setData(path2, Bytes.add(Bytes.toBytes(path2), Bytes.toBytes(path2))));
138    // delete
139    ops.add(ZKUtilOp.deleteNodeFailSilent(path3));
140    ops.add(ZKUtilOp.deleteNodeFailSilent(path4));
141    // create
142    ops.add(ZKUtilOp.createAndFailSilent(path5, Bytes.toBytes(path5)));
143    ops.add(ZKUtilOp.createAndFailSilent(path6, Bytes.toBytes(path6)));
144    ZKUtil.multiOrSequential(zkw, ops, false);
145    assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1),
146      Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1))));
147    assertTrue(Bytes.equals(ZKUtil.getData(zkw, path2),
148      Bytes.add(Bytes.toBytes(path2), Bytes.toBytes(path2))));
149    assertEquals(-1, ZKUtil.checkExists(zkw, path3));
150    assertEquals(-1, ZKUtil.checkExists(zkw, path4));
151    assertTrue(Bytes.equals(ZKUtil.getData(zkw, path5), Bytes.toBytes(path5)));
152    assertTrue(Bytes.equals(ZKUtil.getData(zkw, path6), Bytes.toBytes(path6)));
153  }
154
155  @Test
156  public void testSingleFailure() throws Exception {
157    // try to delete a node that doesn't exist
158    boolean caughtNoNode = false;
159    String path = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testSingleFailureZ");
160    LinkedList<ZKUtilOp> ops = new LinkedList<>();
161    ops.add(ZKUtilOp.deleteNodeFailSilent(path));
162    try {
163      ZKUtil.multiOrSequential(zkw, ops, false);
164    } catch (KeeperException.NoNodeException nne) {
165      caughtNoNode = true;
166    }
167    assertTrue(caughtNoNode);
168
169    // try to setData on a node that doesn't exist
170    caughtNoNode = false;
171    ops = new LinkedList<>();
172    ops.add(ZKUtilOp.setData(path, Bytes.toBytes(path)));
173    try {
174      ZKUtil.multiOrSequential(zkw, ops, false);
175    } catch (KeeperException.NoNodeException nne) {
176      caughtNoNode = true;
177    }
178    assertTrue(caughtNoNode);
179
180    // try to create on a node that already exists
181    boolean caughtNodeExists = false;
182    ops = new LinkedList<>();
183    ops.add(ZKUtilOp.createAndFailSilent(path, Bytes.toBytes(path)));
184    ZKUtil.multiOrSequential(zkw, ops, false);
185    try {
186      ZKUtil.multiOrSequential(zkw, ops, false);
187    } catch (KeeperException.NodeExistsException nee) {
188      caughtNodeExists = true;
189    }
190    assertTrue(caughtNodeExists);
191  }
192
193  @Test
194  public void testSingleFailureInMulti() throws Exception {
195    // try a multi where all but one operation succeeds
196    String pathA = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testSingleFailureInMultiA");
197    String pathB = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testSingleFailureInMultiB");
198    String pathC = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testSingleFailureInMultiC");
199    LinkedList<ZKUtilOp> ops = new LinkedList<>();
200    ops.add(ZKUtilOp.createAndFailSilent(pathA, Bytes.toBytes(pathA)));
201    ops.add(ZKUtilOp.createAndFailSilent(pathB, Bytes.toBytes(pathB)));
202    ops.add(ZKUtilOp.deleteNodeFailSilent(pathC));
203    boolean caughtNoNode = false;
204    try {
205      ZKUtil.multiOrSequential(zkw, ops, false);
206    } catch (KeeperException.NoNodeException nne) {
207      caughtNoNode = true;
208    }
209    assertTrue(caughtNoNode);
210    // assert that none of the operations succeeded
211    assertEquals(-1, ZKUtil.checkExists(zkw, pathA));
212    assertEquals(-1, ZKUtil.checkExists(zkw, pathB));
213    assertEquals(-1, ZKUtil.checkExists(zkw, pathC));
214  }
215
216  @Test
217  public void testMultiFailure() throws Exception {
218    String pathX = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testMultiFailureX");
219    String pathY = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testMultiFailureY");
220    String pathZ = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testMultiFailureZ");
221    // create X that we will use to fail create later
222    LinkedList<ZKUtilOp> ops = new LinkedList<>();
223    ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX)));
224    ZKUtil.multiOrSequential(zkw, ops, false);
225
226    // fail one of each create ,setData, delete
227    String pathV = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testMultiFailureV");
228    String pathW = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testMultiFailureW");
229    ops = new LinkedList<>();
230    ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX))); // fail  -- already exists
231    ops.add(ZKUtilOp.setData(pathY, Bytes.toBytes(pathY))); // fail -- doesn't exist
232    ops.add(ZKUtilOp.deleteNodeFailSilent(pathZ)); // fail -- doesn't exist
233    ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathV))); // pass
234    ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathW))); // pass
235    boolean caughtNodeExists = false;
236    try {
237      ZKUtil.multiOrSequential(zkw, ops, false);
238    } catch (KeeperException.NodeExistsException nee) {
239      // check first operation that fails throws exception
240      caughtNodeExists = true;
241    }
242    assertTrue(caughtNodeExists);
243    // check that no modifications were made
244    assertNotEquals(-1, ZKUtil.checkExists(zkw, pathX));
245    assertEquals(-1, ZKUtil.checkExists(zkw, pathY));
246    assertEquals(-1, ZKUtil.checkExists(zkw, pathZ));
247    assertEquals(-1, ZKUtil.checkExists(zkw, pathW));
248    assertEquals(-1, ZKUtil.checkExists(zkw, pathV));
249
250    // test that with multiple failures, throws an exception corresponding to first failure in list
251    ops = new LinkedList<>();
252    ops.add(ZKUtilOp.setData(pathY, Bytes.toBytes(pathY))); // fail -- doesn't exist
253    ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX))); // fail -- exists
254    boolean caughtNoNode = false;
255    try {
256      ZKUtil.multiOrSequential(zkw, ops, false);
257    } catch (KeeperException.NoNodeException nne) {
258      // check first operation that fails throws exception
259      caughtNoNode = true;
260    }
261    assertTrue(caughtNoNode);
262    // check that no modifications were made
263    assertNotEquals(-1, ZKUtil.checkExists(zkw, pathX));
264    assertEquals(-1, ZKUtil.checkExists(zkw, pathY));
265    assertEquals(-1, ZKUtil.checkExists(zkw, pathZ));
266    assertEquals(-1, ZKUtil.checkExists(zkw, pathW));
267    assertEquals(-1, ZKUtil.checkExists(zkw, pathV));
268  }
269
270  @Test
271  public void testRunSequentialOnMultiFailure() throws Exception {
272    String path1 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "runSequential1");
273    String path2 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "runSequential2");
274    String path3 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "runSequential3");
275    String path4 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "runSequential4");
276
277    // create some nodes that we will use later
278    LinkedList<ZKUtilOp> ops = new LinkedList<>();
279    ops.add(ZKUtilOp.createAndFailSilent(path1, Bytes.toBytes(path1)));
280    ops.add(ZKUtilOp.createAndFailSilent(path2, Bytes.toBytes(path2)));
281    ZKUtil.multiOrSequential(zkw, ops, false);
282
283    // test that, even with operations that fail, the ones that would pass will pass
284    // with runSequentialOnMultiFailure
285    ops = new LinkedList<>();
286    ops.add(ZKUtilOp.setData(path1, Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1)))); // pass
287    ops.add(ZKUtilOp.deleteNodeFailSilent(path2)); // pass
288    ops.add(ZKUtilOp.deleteNodeFailSilent(path3)); // fail -- node doesn't exist
289    ops.add(ZKUtilOp.createAndFailSilent(path4,
290      Bytes.add(Bytes.toBytes(path4), Bytes.toBytes(path4)))); // pass
291    ZKUtil.multiOrSequential(zkw, ops, true);
292    assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1),
293      Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1))));
294    assertEquals(-1, ZKUtil.checkExists(zkw, path2));
295    assertEquals(-1, ZKUtil.checkExists(zkw, path3));
296    assertNotEquals(-1, ZKUtil.checkExists(zkw, path4));
297  }
298
299  /**
300   * Verifies that for the given root node, it should delete all the child nodes
301   * recursively using multi-update api.
302   */
303  @Test
304  public void testdeleteChildrenRecursivelyMulti() throws Exception {
305    String parentZNode = "/testRootMulti";
306    createZNodeTree(parentZNode);
307
308    ZKUtil.deleteChildrenRecursivelyMultiOrSequential(zkw, true, parentZNode);
309
310    assertTrue("Wrongly deleted parent znode!",
311        ZKUtil.checkExists(zkw, parentZNode) > -1);
312    List<String> children = zkw.getRecoverableZooKeeper().getChildren(
313        parentZNode, false);
314    assertEquals("Failed to delete child znodes!", 0, children.size());
315  }
316
317  /**
318   * Verifies that for the given root node, it should delete all the nodes recursively using
319   * multi-update api.
320   */
321  @Test
322  public void testDeleteNodeRecursivelyMulti() throws Exception {
323    String parentZNode = "/testdeleteNodeRecursivelyMulti";
324    createZNodeTree(parentZNode);
325
326    ZKUtil.deleteNodeRecursively(zkw, parentZNode);
327    assertEquals("Parent znode should be deleted.", -1, ZKUtil.checkExists(zkw, parentZNode));
328  }
329
330  @Test
331  public void testDeleteNodeRecursivelyMultiOrSequential() throws Exception {
332    String parentZNode1 = "/testdeleteNode1";
333    String parentZNode2 = "/testdeleteNode2";
334    String parentZNode3 = "/testdeleteNode3";
335    createZNodeTree(parentZNode1);
336    createZNodeTree(parentZNode2);
337    createZNodeTree(parentZNode3);
338
339    ZKUtil.deleteNodeRecursivelyMultiOrSequential(zkw, false, parentZNode1, parentZNode2,
340      parentZNode3);
341    assertEquals("Parent znode 1 should be deleted.", -1, ZKUtil.checkExists(zkw, parentZNode1));
342    assertEquals("Parent znode 2 should be deleted.", -1, ZKUtil.checkExists(zkw, parentZNode2));
343    assertEquals("Parent znode 3 should be deleted.", -1, ZKUtil.checkExists(zkw, parentZNode3));
344  }
345
346  @Test
347  public void testDeleteChildrenRecursivelyMultiOrSequential() throws Exception {
348    String parentZNode1 = "/testdeleteChildren1";
349    String parentZNode2 = "/testdeleteChildren2";
350    String parentZNode3 = "/testdeleteChildren3";
351    createZNodeTree(parentZNode1);
352    createZNodeTree(parentZNode2);
353    createZNodeTree(parentZNode3);
354
355    ZKUtil.deleteChildrenRecursivelyMultiOrSequential(zkw, true, parentZNode1, parentZNode2,
356      parentZNode3);
357
358    assertTrue("Wrongly deleted parent znode 1!", ZKUtil.checkExists(zkw, parentZNode1) > -1);
359    List<String> children = zkw.getRecoverableZooKeeper().getChildren(parentZNode1, false);
360    assertEquals("Failed to delete child znodes of parent znode 1!", 0, children.size());
361
362    assertTrue("Wrongly deleted parent znode 2!", ZKUtil.checkExists(zkw, parentZNode2) > -1);
363    children = zkw.getRecoverableZooKeeper().getChildren(parentZNode2, false);
364    assertEquals("Failed to delete child znodes of parent znode 1!", 0, children.size());
365
366    assertTrue("Wrongly deleted parent znode 3!", ZKUtil.checkExists(zkw, parentZNode3) > -1);
367    children = zkw.getRecoverableZooKeeper().getChildren(parentZNode3, false);
368    assertEquals("Failed to delete child znodes of parent znode 1!", 0, children.size());
369  }
370
371  private void createZNodeTree(String rootZNode) throws KeeperException,
372      InterruptedException {
373    List<Op> opList = new ArrayList<>();
374    opList.add(Op.create(rootZNode, new byte[0], Ids.OPEN_ACL_UNSAFE,
375        CreateMode.PERSISTENT));
376    int level = 0;
377    String parentZNode = rootZNode;
378    while (level < 10) {
379      // define parent node
380      parentZNode = parentZNode + "/" + level;
381      opList.add(Op.create(parentZNode, new byte[0], Ids.OPEN_ACL_UNSAFE,
382          CreateMode.PERSISTENT));
383      int elements = 0;
384      // add elements to the parent node
385      while (elements < level) {
386        opList.add(Op.create(parentZNode + "/" + elements, new byte[0],
387            Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT));
388        elements++;
389      }
390      level++;
391    }
392    zkw.getRecoverableZooKeeper().multi(opList);
393  }
394}