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.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertNotEquals; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collections; 027import java.util.LinkedList; 028import java.util.List; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.hadoop.hbase.Abortable; 031import org.apache.hadoop.hbase.HBaseZKTestingUtil; 032import org.apache.hadoop.hbase.testclassification.MediumTests; 033import org.apache.hadoop.hbase.testclassification.ZKTests; 034import org.apache.hadoop.hbase.util.Bytes; 035import org.apache.hadoop.hbase.zookeeper.ZKUtil.ZKUtilOp; 036import org.apache.zookeeper.CreateMode; 037import org.apache.zookeeper.KeeperException; 038import org.apache.zookeeper.Op; 039import org.apache.zookeeper.ZooDefs.Ids; 040import org.junit.jupiter.api.AfterAll; 041import org.junit.jupiter.api.BeforeAll; 042import org.junit.jupiter.api.Tag; 043import org.junit.jupiter.api.Test; 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047/** 048 * Test ZooKeeper multi-update functionality. 049 */ 050@Tag(ZKTests.TAG) 051@Tag(MediumTests.TAG) 052public class TestZKMulti { 053 054 private static final Logger LOG = LoggerFactory.getLogger(TestZKMulti.class); 055 private final static HBaseZKTestingUtil TEST_UTIL = new HBaseZKTestingUtil(); 056 private static ZKWatcher zkw = null; 057 058 private static class ZKMultiAbortable implements Abortable { 059 @Override 060 public void abort(String why, Throwable e) { 061 LOG.info(why, e); 062 } 063 064 @Override 065 public boolean isAborted() { 066 return false; 067 } 068 } 069 070 @BeforeAll 071 public static void setUpBeforeClass() throws Exception { 072 TEST_UTIL.startMiniZKCluster(); 073 Configuration conf = TEST_UTIL.getConfiguration(); 074 Abortable abortable = new ZKMultiAbortable(); 075 zkw = new ZKWatcher(conf, "TestZKMulti", abortable, true); 076 } 077 078 @AfterAll 079 public static void tearDownAfterClass() throws Exception { 080 TEST_UTIL.shutdownMiniZKCluster(); 081 } 082 083 @Test 084 public void testSimpleMulti() throws Exception { 085 // null multi 086 ZKUtil.multiOrSequential(zkw, null, false); 087 088 // empty multi 089 ZKUtil.multiOrSequential(zkw, new LinkedList<>(), false); 090 091 // single create 092 String path = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testSimpleMulti"); 093 LinkedList<ZKUtilOp> singleCreate = new LinkedList<>(); 094 singleCreate.add(ZKUtilOp.createAndFailSilent(path, new byte[0])); 095 ZKUtil.multiOrSequential(zkw, singleCreate, false); 096 assertTrue(ZKUtil.checkExists(zkw, path) != -1); 097 098 // single setdata 099 LinkedList<ZKUtilOp> singleSetData = new LinkedList<>(); 100 byte[] data = Bytes.toBytes("foobar"); 101 singleSetData.add(ZKUtilOp.setData(path, data)); 102 ZKUtil.multiOrSequential(zkw, singleSetData, false); 103 assertTrue(Bytes.equals(ZKUtil.getData(zkw, path), data)); 104 105 // single delete 106 LinkedList<ZKUtilOp> singleDelete = new LinkedList<>(); 107 singleDelete.add(ZKUtilOp.deleteNodeFailSilent(path)); 108 ZKUtil.multiOrSequential(zkw, singleDelete, false); 109 assertEquals(-1, ZKUtil.checkExists(zkw, path)); 110 } 111 112 @Test 113 public void testComplexMulti() throws Exception { 114 String path1 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti1"); 115 String path2 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti2"); 116 String path3 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti3"); 117 String path4 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti4"); 118 String path5 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti5"); 119 String path6 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testComplexMulti6"); 120 // create 4 nodes that we'll setData on or delete later 121 LinkedList<ZKUtilOp> create4Nodes = new LinkedList<>(); 122 create4Nodes.add(ZKUtilOp.createAndFailSilent(path1, Bytes.toBytes(path1))); 123 create4Nodes.add(ZKUtilOp.createAndFailSilent(path2, Bytes.toBytes(path2))); 124 create4Nodes.add(ZKUtilOp.createAndFailSilent(path3, Bytes.toBytes(path3))); 125 create4Nodes.add(ZKUtilOp.createAndFailSilent(path4, Bytes.toBytes(path4))); 126 ZKUtil.multiOrSequential(zkw, create4Nodes, false); 127 assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1), Bytes.toBytes(path1))); 128 assertTrue(Bytes.equals(ZKUtil.getData(zkw, path2), Bytes.toBytes(path2))); 129 assertTrue(Bytes.equals(ZKUtil.getData(zkw, path3), Bytes.toBytes(path3))); 130 assertTrue(Bytes.equals(ZKUtil.getData(zkw, path4), Bytes.toBytes(path4))); 131 132 // do multiple of each operation (setData, delete, create) 133 LinkedList<ZKUtilOp> ops = new LinkedList<>(); 134 // setData 135 ops.add(ZKUtilOp.setData(path1, Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1)))); 136 ops.add(ZKUtilOp.setData(path2, Bytes.add(Bytes.toBytes(path2), Bytes.toBytes(path2)))); 137 // delete 138 ops.add(ZKUtilOp.deleteNodeFailSilent(path3)); 139 ops.add(ZKUtilOp.deleteNodeFailSilent(path4)); 140 // create 141 ops.add(ZKUtilOp.createAndFailSilent(path5, Bytes.toBytes(path5))); 142 ops.add(ZKUtilOp.createAndFailSilent(path6, Bytes.toBytes(path6))); 143 ZKUtil.multiOrSequential(zkw, ops, false); 144 assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1), 145 Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1)))); 146 assertTrue(Bytes.equals(ZKUtil.getData(zkw, path2), 147 Bytes.add(Bytes.toBytes(path2), Bytes.toBytes(path2)))); 148 assertEquals(-1, ZKUtil.checkExists(zkw, path3)); 149 assertEquals(-1, ZKUtil.checkExists(zkw, path4)); 150 assertTrue(Bytes.equals(ZKUtil.getData(zkw, path5), Bytes.toBytes(path5))); 151 assertTrue(Bytes.equals(ZKUtil.getData(zkw, path6), Bytes.toBytes(path6))); 152 } 153 154 @Test 155 public void testSingleFailure() throws Exception { 156 // try to delete a node that doesn't exist 157 boolean caughtNoNode = false; 158 String path = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testSingleFailureZ"); 159 LinkedList<ZKUtilOp> ops = new LinkedList<>(); 160 ops.add(ZKUtilOp.deleteNodeFailSilent(path)); 161 try { 162 ZKUtil.multiOrSequential(zkw, ops, false); 163 } catch (KeeperException.NoNodeException nne) { 164 caughtNoNode = true; 165 } 166 assertTrue(caughtNoNode); 167 168 // try to setData on a node that doesn't exist 169 caughtNoNode = false; 170 ops = new LinkedList<>(); 171 ops.add(ZKUtilOp.setData(path, Bytes.toBytes(path))); 172 try { 173 ZKUtil.multiOrSequential(zkw, ops, false); 174 } catch (KeeperException.NoNodeException nne) { 175 caughtNoNode = true; 176 } 177 assertTrue(caughtNoNode); 178 179 // try to create on a node that already exists 180 boolean caughtNodeExists = false; 181 ops = new LinkedList<>(); 182 ops.add(ZKUtilOp.createAndFailSilent(path, Bytes.toBytes(path))); 183 ZKUtil.multiOrSequential(zkw, ops, false); 184 try { 185 ZKUtil.multiOrSequential(zkw, ops, false); 186 } catch (KeeperException.NodeExistsException nee) { 187 caughtNodeExists = true; 188 } 189 assertTrue(caughtNodeExists); 190 } 191 192 @Test 193 public void testSingleFailureInMulti() throws Exception { 194 // try a multi where all but one operation succeeds 195 String pathA = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testSingleFailureInMultiA"); 196 String pathB = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testSingleFailureInMultiB"); 197 String pathC = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testSingleFailureInMultiC"); 198 LinkedList<ZKUtilOp> ops = new LinkedList<>(); 199 ops.add(ZKUtilOp.createAndFailSilent(pathA, Bytes.toBytes(pathA))); 200 ops.add(ZKUtilOp.createAndFailSilent(pathB, Bytes.toBytes(pathB))); 201 ops.add(ZKUtilOp.deleteNodeFailSilent(pathC)); 202 boolean caughtNoNode = false; 203 try { 204 ZKUtil.multiOrSequential(zkw, ops, false); 205 } catch (KeeperException.NoNodeException nne) { 206 caughtNoNode = true; 207 } 208 assertTrue(caughtNoNode); 209 // assert that none of the operations succeeded 210 assertEquals(-1, ZKUtil.checkExists(zkw, pathA)); 211 assertEquals(-1, ZKUtil.checkExists(zkw, pathB)); 212 assertEquals(-1, ZKUtil.checkExists(zkw, pathC)); 213 } 214 215 @Test 216 public void testMultiFailure() throws Exception { 217 String pathX = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testMultiFailureX"); 218 String pathY = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testMultiFailureY"); 219 String pathZ = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testMultiFailureZ"); 220 // create X that we will use to fail create later 221 LinkedList<ZKUtilOp> ops = new LinkedList<>(); 222 ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX))); 223 ZKUtil.multiOrSequential(zkw, ops, false); 224 225 // fail one of each create ,setData, delete 226 String pathV = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testMultiFailureV"); 227 String pathW = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "testMultiFailureW"); 228 ops = new LinkedList<>(); 229 ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX))); // fail -- already exists 230 ops.add(ZKUtilOp.setData(pathY, Bytes.toBytes(pathY))); // fail -- doesn't exist 231 ops.add(ZKUtilOp.deleteNodeFailSilent(pathZ)); // fail -- doesn't exist 232 ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathV))); // pass 233 ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathW))); // pass 234 boolean caughtNodeExists = false; 235 try { 236 ZKUtil.multiOrSequential(zkw, ops, false); 237 } catch (KeeperException.NodeExistsException nee) { 238 // check first operation that fails throws exception 239 caughtNodeExists = true; 240 } 241 assertTrue(caughtNodeExists); 242 // check that no modifications were made 243 assertNotEquals(-1, ZKUtil.checkExists(zkw, pathX)); 244 assertEquals(-1, ZKUtil.checkExists(zkw, pathY)); 245 assertEquals(-1, ZKUtil.checkExists(zkw, pathZ)); 246 assertEquals(-1, ZKUtil.checkExists(zkw, pathW)); 247 assertEquals(-1, ZKUtil.checkExists(zkw, pathV)); 248 249 // test that with multiple failures, throws an exception corresponding to first failure in list 250 ops = new LinkedList<>(); 251 ops.add(ZKUtilOp.setData(pathY, Bytes.toBytes(pathY))); // fail -- doesn't exist 252 ops.add(ZKUtilOp.createAndFailSilent(pathX, Bytes.toBytes(pathX))); // fail -- exists 253 boolean caughtNoNode = false; 254 try { 255 ZKUtil.multiOrSequential(zkw, ops, false); 256 } catch (KeeperException.NoNodeException nne) { 257 // check first operation that fails throws exception 258 caughtNoNode = true; 259 } 260 assertTrue(caughtNoNode); 261 // check that no modifications were made 262 assertNotEquals(-1, ZKUtil.checkExists(zkw, pathX)); 263 assertEquals(-1, ZKUtil.checkExists(zkw, pathY)); 264 assertEquals(-1, ZKUtil.checkExists(zkw, pathZ)); 265 assertEquals(-1, ZKUtil.checkExists(zkw, pathW)); 266 assertEquals(-1, ZKUtil.checkExists(zkw, pathV)); 267 } 268 269 @Test 270 public void testRunSequentialOnMultiFailure() throws Exception { 271 String path1 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "runSequential1"); 272 String path2 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "runSequential2"); 273 String path3 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "runSequential3"); 274 String path4 = ZNodePaths.joinZNode(zkw.getZNodePaths().baseZNode, "runSequential4"); 275 276 // create some nodes that we will use later 277 LinkedList<ZKUtilOp> ops = new LinkedList<>(); 278 ops.add(ZKUtilOp.createAndFailSilent(path1, Bytes.toBytes(path1))); 279 ops.add(ZKUtilOp.createAndFailSilent(path2, Bytes.toBytes(path2))); 280 ZKUtil.multiOrSequential(zkw, ops, false); 281 282 // test that, even with operations that fail, the ones that would pass will pass 283 // with runSequentialOnMultiFailure 284 ops = new LinkedList<>(); 285 // pass 286 ops.add(ZKUtilOp.setData(path1, Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1)))); 287 // pass 288 ops.add(ZKUtilOp.deleteNodeFailSilent(path2)); 289 // fail -- node doesn't exist 290 ops.add(ZKUtilOp.deleteNodeFailSilent(path3)); 291 // pass 292 ops.add( 293 ZKUtilOp.createAndFailSilent(path4, Bytes.add(Bytes.toBytes(path4), Bytes.toBytes(path4)))); 294 ZKUtil.multiOrSequential(zkw, ops, true); 295 assertTrue(Bytes.equals(ZKUtil.getData(zkw, path1), 296 Bytes.add(Bytes.toBytes(path1), Bytes.toBytes(path1)))); 297 assertEquals(-1, ZKUtil.checkExists(zkw, path2)); 298 assertEquals(-1, ZKUtil.checkExists(zkw, path3)); 299 assertNotEquals(-1, ZKUtil.checkExists(zkw, path4)); 300 } 301 302 /** 303 * Verifies that for the given root node, it should delete all the child nodes recursively using 304 * multi-update api. 305 */ 306 @Test 307 public void testdeleteChildrenRecursivelyMulti() throws Exception { 308 String parentZNode = "/testRootMulti"; 309 createZNodeTree(parentZNode); 310 311 ZKUtil.deleteChildrenRecursivelyMultiOrSequential(zkw, true, parentZNode); 312 313 assertTrue(ZKUtil.checkExists(zkw, parentZNode) > -1, "Wrongly deleted parent znode!"); 314 List<String> children = zkw.getRecoverableZooKeeper().getChildren(parentZNode, false); 315 assertEquals(0, children.size(), "Failed to delete child znodes!"); 316 } 317 318 /** 319 * Verifies that for the given root node, it should delete all the nodes recursively using 320 * multi-update api. 321 */ 322 @Test 323 public void testDeleteNodeRecursivelyMulti() throws Exception { 324 String parentZNode = "/testdeleteNodeRecursivelyMulti"; 325 createZNodeTree(parentZNode); 326 327 ZKUtil.deleteNodeRecursively(zkw, parentZNode); 328 assertEquals(-1, ZKUtil.checkExists(zkw, parentZNode), "Parent znode should be deleted."); 329 } 330 331 @Test 332 public void testDeleteNodeRecursivelyMultiOrSequential() throws Exception { 333 String parentZNode1 = "/testdeleteNode1"; 334 String parentZNode2 = "/testdeleteNode2"; 335 String parentZNode3 = "/testdeleteNode3"; 336 createZNodeTree(parentZNode1); 337 createZNodeTree(parentZNode2); 338 createZNodeTree(parentZNode3); 339 340 ZKUtil.deleteNodeRecursivelyMultiOrSequential(zkw, false, parentZNode1, parentZNode2, 341 parentZNode3); 342 assertEquals(-1, ZKUtil.checkExists(zkw, parentZNode1), "Parent znode 1 should be deleted."); 343 assertEquals(-1, ZKUtil.checkExists(zkw, parentZNode2), "Parent znode 2 should be deleted."); 344 assertEquals(-1, ZKUtil.checkExists(zkw, parentZNode3), "Parent znode 3 should be deleted."); 345 } 346 347 @Test 348 public void testDeleteChildrenRecursivelyMultiOrSequential() throws Exception { 349 String parentZNode1 = "/testdeleteChildren1"; 350 String parentZNode2 = "/testdeleteChildren2"; 351 String parentZNode3 = "/testdeleteChildren3"; 352 createZNodeTree(parentZNode1); 353 createZNodeTree(parentZNode2); 354 createZNodeTree(parentZNode3); 355 356 ZKUtil.deleteChildrenRecursivelyMultiOrSequential(zkw, true, parentZNode1, parentZNode2, 357 parentZNode3); 358 359 assertTrue(ZKUtil.checkExists(zkw, parentZNode1) > -1, "Wrongly deleted parent znode 1!"); 360 List<String> children = zkw.getRecoverableZooKeeper().getChildren(parentZNode1, false); 361 assertEquals(0, children.size(), "Failed to delete child znodes of parent znode 1!"); 362 363 assertTrue(ZKUtil.checkExists(zkw, parentZNode2) > -1, "Wrongly deleted parent znode 2!"); 364 children = zkw.getRecoverableZooKeeper().getChildren(parentZNode2, false); 365 assertEquals(0, children.size(), "Failed to delete child znodes of parent znode 1!"); 366 367 assertTrue(ZKUtil.checkExists(zkw, parentZNode3) > -1, "Wrongly deleted parent znode 3!"); 368 children = zkw.getRecoverableZooKeeper().getChildren(parentZNode3, false); 369 assertEquals(0, children.size(), "Failed to delete child znodes of parent znode 1!"); 370 } 371 372 @Test 373 public void testBatchedDeletesOfWideZNodes() throws Exception { 374 // Batch every 50bytes 375 final int batchSize = 50; 376 Configuration localConf = new Configuration(TEST_UTIL.getConfiguration()); 377 localConf.setInt("zookeeper.multi.max.size", batchSize); 378 try (ZKWatcher customZkw = 379 new ZKWatcher(localConf, "TestZKMulti_Custom", new ZKMultiAbortable(), true)) { 380 381 // With a parent znode like this, we'll get batches of 2-3 elements 382 final String parent1 = "/batchedDeletes1"; 383 final String parent2 = "/batchedDeletes2"; 384 final byte[] EMPTY_BYTES = new byte[0]; 385 386 // Write one node 387 List<Op> ops = new ArrayList<>(); 388 ops.add(Op.create(parent1, EMPTY_BYTES, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); 389 for (int i = 0; i < batchSize * 2; i++) { 390 ops.add( 391 Op.create(parent1 + "/" + i, EMPTY_BYTES, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); 392 } 393 customZkw.getRecoverableZooKeeper().multi(ops); 394 395 // Write into a second node 396 ops.clear(); 397 ops.add(Op.create(parent2, EMPTY_BYTES, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); 398 for (int i = 0; i < batchSize * 4; i++) { 399 ops.add( 400 Op.create(parent2 + "/" + i, EMPTY_BYTES, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); 401 } 402 customZkw.getRecoverableZooKeeper().multi(ops); 403 404 // These should return successfully 405 ZKUtil.deleteChildrenRecursively(customZkw, parent1); 406 ZKUtil.deleteChildrenRecursively(customZkw, parent2); 407 } 408 } 409 410 @Test 411 public void testListPartitioning() { 412 // 10 Bytes 413 ZKUtilOp tenByteOp = ZKUtilOp.deleteNodeFailSilent("/123456789"); 414 415 // Simple, single element case 416 assertEquals(Collections.singletonList(Collections.singletonList(tenByteOp)), 417 ZKUtil.partitionOps(Collections.singletonList(tenByteOp), 15)); 418 419 // Simple case where we exceed the limit, but must make the list 420 assertEquals(Collections.singletonList(Collections.singletonList(tenByteOp)), 421 ZKUtil.partitionOps(Collections.singletonList(tenByteOp), 5)); 422 423 // Each gets its own bucket 424 assertEquals( 425 Arrays.asList(Collections.singletonList(tenByteOp), Collections.singletonList(tenByteOp), 426 Collections.singletonList(tenByteOp)), 427 ZKUtil.partitionOps(Arrays.asList(tenByteOp, tenByteOp, tenByteOp), 15)); 428 429 // Test internal boundary 430 assertEquals( 431 Arrays.asList(Arrays.asList(tenByteOp, tenByteOp), Collections.singletonList(tenByteOp)), 432 ZKUtil.partitionOps(Arrays.asList(tenByteOp, tenByteOp, tenByteOp), 20)); 433 434 // Plenty of space for one partition 435 assertEquals(Collections.singletonList(Arrays.asList(tenByteOp, tenByteOp, tenByteOp)), 436 ZKUtil.partitionOps(Arrays.asList(tenByteOp, tenByteOp, tenByteOp), 50)); 437 } 438 439 private void createZNodeTree(String rootZNode) throws KeeperException, InterruptedException { 440 List<Op> opList = new ArrayList<>(); 441 opList.add(Op.create(rootZNode, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); 442 int level = 0; 443 String parentZNode = rootZNode; 444 while (level < 10) { 445 // define parent node 446 parentZNode = parentZNode + "/" + level; 447 opList.add(Op.create(parentZNode, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); 448 int elements = 0; 449 // add elements to the parent node 450 while (elements < level) { 451 opList.add(Op.create(parentZNode + "/" + elements, new byte[0], Ids.OPEN_ACL_UNSAFE, 452 CreateMode.PERSISTENT)); 453 elements++; 454 } 455 level++; 456 } 457 zkw.getRecoverableZooKeeper().multi(opList); 458 } 459}