View Javadoc

1   /**
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.zookeeper;
22  
23  
24  import static org.junit.Assert.assertFalse;
25  import static org.junit.Assert.assertTrue;
26  
27  import java.util.ArrayList;
28  import java.util.LinkedList;
29  import java.util.List;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.hadoop.conf.Configuration;
34  import org.apache.hadoop.hbase.Abortable;
35  import org.apache.hadoop.hbase.HBaseTestingUtility;
36  import org.apache.hadoop.hbase.testclassification.MediumTests;
37  import org.apache.hadoop.hbase.util.Bytes;
38  import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp;
39  import org.apache.zookeeper.CreateMode;
40  import org.apache.zookeeper.KeeperException;
41  import org.apache.zookeeper.Op;
42  import org.apache.zookeeper.ZooDefs.Ids;
43  import org.junit.AfterClass;
44  import org.junit.BeforeClass;
45  import org.junit.Test;
46  import org.junit.experimental.categories.Category;
47  
48  /**
49   * Test ZooKeeper multi-update functionality
50   */
51  @Category(MediumTests.class)
52  public class TestZKMulti {
53    private static final Log LOG = LogFactory.getLog(TestZKMulti.class);
54    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
55    private static ZooKeeperWatcher zkw = null;
56  
57    @BeforeClass
58    public static void setUpBeforeClass() throws Exception {
59      TEST_UTIL.startMiniZKCluster();
60      Configuration conf = TEST_UTIL.getConfiguration();
61      conf.setBoolean("hbase.zookeeper.useMulti", true);
62      Abortable abortable = new Abortable() {
63        @Override
64        public void abort(String why, Throwable e) {
65          LOG.info(why, e);
66        }
67  
68        @Override
69        public boolean isAborted() {
70          return false;
71        }
72      };
73      zkw = new ZooKeeperWatcher(conf,
74        "TestZKMulti", abortable, true);
75    }
76  
77    @AfterClass
78    public static void tearDownAfterClass() throws Exception {
79      TEST_UTIL.shutdownMiniZKCluster();
80    }
81  
82    @Test (timeout=60000)
83    public void testSimpleMulti() throws Exception {
84      // null multi
85      ZKUtil.multiOrSequential(zkw, null, false);
86  
87      // empty multi
88      ZKUtil.multiOrSequential(zkw, new LinkedList<ZKUtilOp>(), false);
89  
90      // single create
91      String path = ZKUtil.joinZNode(zkw.baseZNode, "testSimpleMulti");
92      LinkedList<ZKUtilOp> singleCreate = new LinkedList<ZKUtilOp>();
93      singleCreate.add(ZKUtilOp.createAndFailSilent(path, new byte[0]));
94      ZKUtil.multiOrSequential(zkw, singleCreate, false);
95      assertTrue(ZKUtil.checkExists(zkw, path) != -1);
96  
97      // single setdata
98      LinkedList<ZKUtilOp> singleSetData = new LinkedList<ZKUtilOp>();
99      byte [] data = Bytes.toBytes("foobar");
100     singleSetData.add(ZKUtilOp.setData(path, data));
101     ZKUtil.multiOrSequential(zkw, singleSetData, false);
102     assertTrue(Bytes.equals(ZKUtil.getData(zkw, path), data));
103 
104     // single delete
105     LinkedList<ZKUtilOp> singleDelete = new LinkedList<ZKUtilOp>();
106     singleDelete.add(ZKUtilOp.deleteNodeFailSilent(path));
107     ZKUtil.multiOrSequential(zkw, singleDelete, false);
108     assertTrue(ZKUtil.checkExists(zkw, path) == -1);
109   }
110 
111   @Test (timeout=60000)
112   public void testComplexMulti() throws Exception {
113     String path1 = ZKUtil.joinZNode(zkw.baseZNode, "testComplexMulti1");
114     String path2 = ZKUtil.joinZNode(zkw.baseZNode, "testComplexMulti2");
115     String path3 = ZKUtil.joinZNode(zkw.baseZNode, "testComplexMulti3");
116     String path4 = ZKUtil.joinZNode(zkw.baseZNode, "testComplexMulti4");
117     String path5 = ZKUtil.joinZNode(zkw.baseZNode, "testComplexMulti5");
118     String path6 = ZKUtil.joinZNode(zkw.baseZNode, "testComplexMulti6");
119     // create 4 nodes that we'll setData on or delete later
120     LinkedList<ZKUtilOp> create4Nodes = new LinkedList<ZKUtilOp>();
121     create4Nodes.add(ZKUtilOp.createAndFailSilent(path1, Bytes.toBytes(path1)));
122     create4Nodes.add(ZKUtilOp.createAndFailSilent(path2, Bytes.toBytes(path2)));
123     create4Nodes.add(ZKUtilOp.createAndFailSilent(path3, Bytes.toBytes(path3)));
124     create4Nodes.add(ZKUtilOp.createAndFailSilent(path4, Bytes.toBytes(path4)));
125     ZKUtil.multiOrSequential(zkw, create4Nodes, false);
126     assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1), Bytes.toBytes(path1)));
127     assertTrue(Bytes.equals(ZKUtil.getData(zkw, path2), Bytes.toBytes(path2)));
128     assertTrue(Bytes.equals(ZKUtil.getData(zkw, path3), Bytes.toBytes(path3)));
129     assertTrue(Bytes.equals(ZKUtil.getData(zkw, path4), Bytes.toBytes(path4)));
130 
131     // do multiple of each operation (setData, delete, create)
132     LinkedList<ZKUtilOp> ops = new LinkedList<ZKUtilOp>();
133     // setData
134     ops.add(ZKUtilOp.setData(path1, Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1))));
135     ops.add(ZKUtilOp.setData(path2, Bytes.add(Bytes.toBytes(path2), Bytes.toBytes(path2))));
136     // delete
137     ops.add(ZKUtilOp.deleteNodeFailSilent(path3));
138     ops.add(ZKUtilOp.deleteNodeFailSilent(path4));
139     // create
140     ops.add(ZKUtilOp.createAndFailSilent(path5, Bytes.toBytes(path5)));
141     ops.add(ZKUtilOp.createAndFailSilent(path6, Bytes.toBytes(path6)));
142     ZKUtil.multiOrSequential(zkw, ops, false);
143     assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1),
144       Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1))));
145     assertTrue(Bytes.equals(ZKUtil.getData(zkw, path2),
146       Bytes.add(Bytes.toBytes(path2), Bytes.toBytes(path2))));
147     assertTrue(ZKUtil.checkExists(zkw, path3) == -1);
148     assertTrue(ZKUtil.checkExists(zkw, path4) == -1);
149     assertTrue(Bytes.equals(ZKUtil.getData(zkw, path5), Bytes.toBytes(path5)));
150     assertTrue(Bytes.equals(ZKUtil.getData(zkw, path6), Bytes.toBytes(path6)));
151   }
152 
153   @Test (timeout=60000)
154   public void testSingleFailure() throws Exception {
155     // try to delete a node that doesn't exist
156     boolean caughtNoNode = false;
157     String path = ZKUtil.joinZNode(zkw.baseZNode, "testSingleFailureZ");
158     LinkedList<ZKUtilOp> ops = new LinkedList<ZKUtilOp>();
159     ops.add(ZKUtilOp.deleteNodeFailSilent(path));
160     try {
161       ZKUtil.multiOrSequential(zkw, ops, false);
162     } catch (KeeperException.NoNodeException nne) {
163       caughtNoNode = true;
164     }
165     assertTrue(caughtNoNode);
166 
167     // try to setData on a node that doesn't exist
168     caughtNoNode = false;
169     ops = new LinkedList<ZKUtilOp>();
170     ops.add(ZKUtilOp.setData(path, Bytes.toBytes(path)));
171     try {
172       ZKUtil.multiOrSequential(zkw, ops, false);
173     } catch (KeeperException.NoNodeException nne) {
174       caughtNoNode = true;
175     }
176     assertTrue(caughtNoNode);
177 
178     // try to create on a node that already exists
179     boolean caughtNodeExists = false;
180     ops = new LinkedList<ZKUtilOp>();
181     ops.add(ZKUtilOp.createAndFailSilent(path, Bytes.toBytes(path)));
182     ZKUtil.multiOrSequential(zkw, ops, false);
183     try {
184       ZKUtil.multiOrSequential(zkw, ops, false);
185     } catch (KeeperException.NodeExistsException nee) {
186       caughtNodeExists = true;
187     }
188     assertTrue(caughtNodeExists);
189   }
190 
191   @Test (timeout=60000)
192   public void testSingleFailureInMulti() throws Exception {
193     // try a multi where all but one operation succeeds
194     String pathA = ZKUtil.joinZNode(zkw.baseZNode, "testSingleFailureInMultiA");
195     String pathB = ZKUtil.joinZNode(zkw.baseZNode, "testSingleFailureInMultiB");
196     String pathC = ZKUtil.joinZNode(zkw.baseZNode, "testSingleFailureInMultiC");
197     LinkedList<ZKUtilOp> ops = new LinkedList<ZKUtilOp>();
198     ops.add(ZKUtilOp.createAndFailSilent(pathA, Bytes.toBytes(pathA)));
199     ops.add(ZKUtilOp.createAndFailSilent(pathB, Bytes.toBytes(pathB)));
200     ops.add(ZKUtilOp.deleteNodeFailSilent(pathC));
201     boolean caughtNoNode = false;
202     try {
203       ZKUtil.multiOrSequential(zkw, ops, false);
204     } catch (KeeperException.NoNodeException nne) {
205       caughtNoNode = true;
206     }
207     assertTrue(caughtNoNode);
208     // assert that none of the operations succeeded
209     assertTrue(ZKUtil.checkExists(zkw, pathA) == -1);
210     assertTrue(ZKUtil.checkExists(zkw, pathB) == -1);
211     assertTrue(ZKUtil.checkExists(zkw, pathC) == -1);
212   }
213 
214   @Test (timeout=60000)
215   public void testMultiFailure() throws Exception {
216     String pathX = ZKUtil.joinZNode(zkw.baseZNode, "testMultiFailureX");
217     String pathY = ZKUtil.joinZNode(zkw.baseZNode, "testMultiFailureY");
218     String pathZ = ZKUtil.joinZNode(zkw.baseZNode, "testMultiFailureZ");
219     // create X that we will use to fail create later
220     LinkedList<ZKUtilOp> ops = new LinkedList<ZKUtilOp>();
221     ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX)));
222     ZKUtil.multiOrSequential(zkw, ops, false);
223 
224     // fail one of each create ,setData, delete
225     String pathV = ZKUtil.joinZNode(zkw.baseZNode, "testMultiFailureV");
226     String pathW = ZKUtil.joinZNode(zkw.baseZNode, "testMultiFailureW");
227     ops = new LinkedList<ZKUtilOp>();
228     ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX))); // fail  -- already exists
229     ops.add(ZKUtilOp.setData(pathY, Bytes.toBytes(pathY))); // fail -- doesn't exist
230     ops.add(ZKUtilOp.deleteNodeFailSilent(pathZ)); // fail -- doesn't exist
231     ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathV))); // pass
232     ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathW))); // pass
233     boolean caughtNodeExists = false;
234     try {
235       ZKUtil.multiOrSequential(zkw, ops, false);
236     } catch (KeeperException.NodeExistsException nee) {
237       // check first operation that fails throws exception
238       caughtNodeExists = true;
239     }
240     assertTrue(caughtNodeExists);
241     // check that no modifications were made
242     assertFalse(ZKUtil.checkExists(zkw, pathX) == -1);
243     assertTrue(ZKUtil.checkExists(zkw, pathY) == -1);
244     assertTrue(ZKUtil.checkExists(zkw, pathZ) == -1);
245     assertTrue(ZKUtil.checkExists(zkw, pathW) == -1);
246     assertTrue(ZKUtil.checkExists(zkw, pathV) == -1);
247 
248     // test that with multiple failures, throws an exception corresponding to first failure in list
249     ops = new LinkedList<ZKUtilOp>();
250     ops.add(ZKUtilOp.setData(pathY, Bytes.toBytes(pathY))); // fail -- doesn't exist
251     ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX))); // fail -- exists
252     boolean caughtNoNode = false;
253     try {
254       ZKUtil.multiOrSequential(zkw, ops, false);
255     } catch (KeeperException.NoNodeException nne) {
256       // check first operation that fails throws exception
257       caughtNoNode = true;
258     }
259     assertTrue(caughtNoNode);
260     // check that no modifications were made
261     assertFalse(ZKUtil.checkExists(zkw, pathX) == -1);
262     assertTrue(ZKUtil.checkExists(zkw, pathY) == -1);
263     assertTrue(ZKUtil.checkExists(zkw, pathZ) == -1);
264     assertTrue(ZKUtil.checkExists(zkw, pathW) == -1);
265     assertTrue(ZKUtil.checkExists(zkw, pathV) == -1);
266   }
267 
268   @Test (timeout=60000)
269   public void testRunSequentialOnMultiFailure() throws Exception {
270     String path1 = ZKUtil.joinZNode(zkw.baseZNode, "runSequential1");
271     String path2 = ZKUtil.joinZNode(zkw.baseZNode, "runSequential2");
272     String path3 = ZKUtil.joinZNode(zkw.baseZNode, "runSequential3");
273     String path4 = ZKUtil.joinZNode(zkw.baseZNode, "runSequential4");
274 
275     // create some nodes that we will use later
276     LinkedList<ZKUtilOp> ops = new LinkedList<ZKUtilOp>();
277     ops.add(ZKUtilOp.createAndFailSilent(path1, Bytes.toBytes(path1)));
278     ops.add(ZKUtilOp.createAndFailSilent(path2, Bytes.toBytes(path2)));
279     ZKUtil.multiOrSequential(zkw, ops, false);
280 
281     // test that, even with operations that fail, the ones that would pass will pass
282     // with runSequentialOnMultiFailure
283     ops = new LinkedList<ZKUtilOp>();
284     ops.add(ZKUtilOp.setData(path1, Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1)))); // pass
285     ops.add(ZKUtilOp.deleteNodeFailSilent(path2)); // pass
286     ops.add(ZKUtilOp.deleteNodeFailSilent(path3)); // fail -- node doesn't exist
287     ops.add(ZKUtilOp.createAndFailSilent(path4,
288       Bytes.add(Bytes.toBytes(path4), Bytes.toBytes(path4)))); // pass
289     ZKUtil.multiOrSequential(zkw, ops, true);
290     assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1),
291       Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1))));
292     assertTrue(ZKUtil.checkExists(zkw, path2) == -1);
293     assertTrue(ZKUtil.checkExists(zkw, path3) == -1);
294     assertFalse(ZKUtil.checkExists(zkw, path4) == -1);
295   }
296 
297   /**
298    * Verifies that for the given root node, it should delete all the child nodes
299    * recursively using multi-update api.
300    */
301   @Test (timeout=60000)
302   public void testdeleteChildrenRecursivelyMulti() throws Exception {
303     String parentZNode = "/testRootMulti";
304     createZNodeTree(parentZNode);
305 
306     ZKUtil.deleteChildrenRecursivelyMultiOrSequential(zkw, true, parentZNode);
307 
308     assertTrue("Wrongly deleted parent znode!",
309         ZKUtil.checkExists(zkw, parentZNode) > -1);
310     List<String> children = zkw.getRecoverableZooKeeper().getChildren(
311         parentZNode, false);
312     assertTrue("Failed to delete child znodes!", 0 == children.size());
313   }
314 
315   /**
316    * Verifies that for the given root node, it should delete all the child nodes
317    * recursively using normal sequential way.
318    */
319   @Test (timeout=60000)
320   public void testdeleteChildrenRecursivelySequential() throws Exception {
321     String parentZNode = "/testRootSeq";
322     createZNodeTree(parentZNode);
323     boolean useMulti = zkw.getConfiguration().getBoolean(
324         "hbase.zookeeper.useMulti", false);
325     zkw.getConfiguration().setBoolean("hbase.zookeeper.useMulti", false);
326     try {
327       // disables the multi-update api execution
328       ZKUtil.deleteChildrenRecursivelyMultiOrSequential(zkw, true, parentZNode);
329 
330       assertTrue("Wrongly deleted parent znode!",
331           ZKUtil.checkExists(zkw, parentZNode) > -1);
332       List<String> children = zkw.getRecoverableZooKeeper().getChildren(
333           parentZNode, false);
334       assertTrue("Failed to delete child znodes!", 0 == children.size());
335     } finally {
336       // sets back the multi-update api execution
337       zkw.getConfiguration().setBoolean("hbase.zookeeper.useMulti", useMulti);
338     }
339   }
340 
341   /**
342    * Verifies that for the given root node, it should delete all the nodes recursively using
343    * multi-update api.
344    */
345   @Test(timeout = 60000)
346   public void testDeleteNodeRecursivelyMulti() throws Exception {
347     String parentZNode = "/testdeleteNodeRecursivelyMulti";
348     createZNodeTree(parentZNode);
349 
350     ZKUtil.deleteNodeRecursively(zkw, parentZNode);
351     assertTrue("Parent znode should be deleted.", ZKUtil.checkExists(zkw, parentZNode) == -1);
352   }
353 
354   /**
355    * Verifies that for the given root node, it should delete all the nodes recursively using
356    * normal sequential way.
357    */
358   @Test(timeout = 60000)
359   public void testDeleteNodeRecursivelySequential() throws Exception {
360     String parentZNode = "/testdeleteNodeRecursivelySequential";
361     createZNodeTree(parentZNode);
362     boolean useMulti = zkw.getConfiguration().getBoolean("hbase.zookeeper.useMulti", false);
363     zkw.getConfiguration().setBoolean("hbase.zookeeper.useMulti", false);
364     try {
365       // disables the multi-update api execution
366       ZKUtil.deleteNodeRecursively(zkw, parentZNode);
367       assertTrue("Parent znode should be deleted.", ZKUtil.checkExists(zkw, parentZNode) == -1);
368     } finally {
369       // sets back the multi-update api execution
370       zkw.getConfiguration().setBoolean("hbase.zookeeper.useMulti", useMulti);
371     }
372   }
373 
374   @Test(timeout = 60000)
375   public void testDeleteNodeRecursivelyMultiOrSequential() throws Exception {
376     String parentZNode1 = "/testdeleteNode1";
377     String parentZNode2 = "/testdeleteNode2";
378     String parentZNode3 = "/testdeleteNode3";
379     createZNodeTree(parentZNode1);
380     createZNodeTree(parentZNode2);
381     createZNodeTree(parentZNode3);
382 
383     ZKUtil.deleteNodeRecursivelyMultiOrSequential(zkw, false, parentZNode1, parentZNode2,
384       parentZNode3);
385     assertTrue("Parent znode 1 should be deleted.", ZKUtil.checkExists(zkw, parentZNode1) == -1);
386     assertTrue("Parent znode 2 should be deleted.", ZKUtil.checkExists(zkw, parentZNode2) == -1);
387     assertTrue("Parent znode 3 should be deleted.", ZKUtil.checkExists(zkw, parentZNode3) == -1);
388   }
389 
390   @Test(timeout = 60000)
391   public void testDeleteChildrenRecursivelyMultiOrSequential() throws Exception {
392     String parentZNode1 = "/testdeleteChildren1";
393     String parentZNode2 = "/testdeleteChildren2";
394     String parentZNode3 = "/testdeleteChildren3";
395     createZNodeTree(parentZNode1);
396     createZNodeTree(parentZNode2);
397     createZNodeTree(parentZNode3);
398 
399     ZKUtil.deleteChildrenRecursivelyMultiOrSequential(zkw, true, parentZNode1, parentZNode2,
400       parentZNode3);
401 
402     assertTrue("Wrongly deleted parent znode 1!", ZKUtil.checkExists(zkw, parentZNode1) > -1);
403     List<String> children = zkw.getRecoverableZooKeeper().getChildren(parentZNode1, false);
404     assertTrue("Failed to delete child znodes of parent znode 1!", 0 == children.size());
405 
406     assertTrue("Wrongly deleted parent znode 2!", ZKUtil.checkExists(zkw, parentZNode2) > -1);
407     children = zkw.getRecoverableZooKeeper().getChildren(parentZNode2, false);
408     assertTrue("Failed to delete child znodes of parent znode 1!", 0 == children.size());
409 
410     assertTrue("Wrongly deleted parent znode 3!", ZKUtil.checkExists(zkw, parentZNode3) > -1);
411     children = zkw.getRecoverableZooKeeper().getChildren(parentZNode3, false);
412     assertTrue("Failed to delete child znodes of parent znode 1!", 0 == children.size());
413   }
414 
415   private void createZNodeTree(String rootZNode) throws KeeperException,
416       InterruptedException {
417     List<Op> opList = new ArrayList<Op>();
418     opList.add(Op.create(rootZNode, new byte[0], Ids.OPEN_ACL_UNSAFE,
419         CreateMode.PERSISTENT));
420     int level = 0;
421     String parentZNode = rootZNode;
422     while (level < 10) {
423       // define parent node
424       parentZNode = parentZNode + "/" + level;
425       opList.add(Op.create(parentZNode, new byte[0], Ids.OPEN_ACL_UNSAFE,
426           CreateMode.PERSISTENT));
427       int elements = 0;
428       // add elements to the parent node
429       while (elements < level) {
430         opList.add(Op.create(parentZNode + "/" + elements, new byte[0],
431             Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT));
432         elements++;
433       }
434       level++;
435     }
436     zkw.getRecoverableZooKeeper().multi(opList);
437   }
438 }