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; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertTrue; 022import static org.junit.Assert.fail; 023 024import edu.umd.cs.findbugs.annotations.Nullable; 025import java.io.File; 026import java.io.IOException; 027import java.io.OutputStream; 028import java.io.UncheckedIOException; 029import java.lang.reflect.Field; 030import java.lang.reflect.Modifier; 031import java.net.BindException; 032import java.net.DatagramSocket; 033import java.net.InetAddress; 034import java.net.ServerSocket; 035import java.net.Socket; 036import java.net.UnknownHostException; 037import java.nio.charset.StandardCharsets; 038import java.security.MessageDigest; 039import java.util.ArrayList; 040import java.util.Arrays; 041import java.util.Collection; 042import java.util.Collections; 043import java.util.HashSet; 044import java.util.Iterator; 045import java.util.List; 046import java.util.Map; 047import java.util.NavigableSet; 048import java.util.Properties; 049import java.util.Random; 050import java.util.Set; 051import java.util.TreeSet; 052import java.util.concurrent.ExecutionException; 053import java.util.concurrent.ThreadLocalRandom; 054import java.util.concurrent.TimeUnit; 055import java.util.concurrent.atomic.AtomicReference; 056import java.util.function.BooleanSupplier; 057import java.util.stream.Collectors; 058import org.apache.commons.io.FileUtils; 059import org.apache.commons.lang3.RandomStringUtils; 060import org.apache.hadoop.conf.Configuration; 061import org.apache.hadoop.fs.FileSystem; 062import org.apache.hadoop.fs.Path; 063import org.apache.hadoop.hbase.Waiter.ExplainingPredicate; 064import org.apache.hadoop.hbase.Waiter.Predicate; 065import org.apache.hadoop.hbase.client.Admin; 066import org.apache.hadoop.hbase.client.AsyncAdmin; 067import org.apache.hadoop.hbase.client.BufferedMutator; 068import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 069import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 070import org.apache.hadoop.hbase.client.Connection; 071import org.apache.hadoop.hbase.client.ConnectionFactory; 072import org.apache.hadoop.hbase.client.Consistency; 073import org.apache.hadoop.hbase.client.Delete; 074import org.apache.hadoop.hbase.client.Durability; 075import org.apache.hadoop.hbase.client.Get; 076import org.apache.hadoop.hbase.client.HBaseAdmin; 077import org.apache.hadoop.hbase.client.Hbck; 078import org.apache.hadoop.hbase.client.ImmutableHRegionInfo; 079import org.apache.hadoop.hbase.client.ImmutableHTableDescriptor; 080import org.apache.hadoop.hbase.client.MasterRegistry; 081import org.apache.hadoop.hbase.client.Put; 082import org.apache.hadoop.hbase.client.RegionInfo; 083import org.apache.hadoop.hbase.client.RegionInfoBuilder; 084import org.apache.hadoop.hbase.client.RegionLocator; 085import org.apache.hadoop.hbase.client.Result; 086import org.apache.hadoop.hbase.client.ResultScanner; 087import org.apache.hadoop.hbase.client.Scan; 088import org.apache.hadoop.hbase.client.Table; 089import org.apache.hadoop.hbase.client.TableDescriptor; 090import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 091import org.apache.hadoop.hbase.client.TableState; 092import org.apache.hadoop.hbase.fs.HFileSystem; 093import org.apache.hadoop.hbase.io.compress.Compression; 094import org.apache.hadoop.hbase.io.compress.Compression.Algorithm; 095import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding; 096import org.apache.hadoop.hbase.io.hfile.BlockCache; 097import org.apache.hadoop.hbase.io.hfile.ChecksumUtil; 098import org.apache.hadoop.hbase.io.hfile.HFile; 099import org.apache.hadoop.hbase.ipc.RpcServerInterface; 100import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException; 101import org.apache.hadoop.hbase.logging.Log4jUtils; 102import org.apache.hadoop.hbase.mapreduce.MapreduceTestingShim; 103import org.apache.hadoop.hbase.master.HMaster; 104import org.apache.hadoop.hbase.master.RegionState; 105import org.apache.hadoop.hbase.master.ServerManager; 106import org.apache.hadoop.hbase.master.assignment.AssignmentManager; 107import org.apache.hadoop.hbase.master.assignment.AssignmentTestingUtil; 108import org.apache.hadoop.hbase.master.assignment.RegionStateStore; 109import org.apache.hadoop.hbase.master.assignment.RegionStates; 110import org.apache.hadoop.hbase.mob.MobFileCache; 111import org.apache.hadoop.hbase.regionserver.BloomType; 112import org.apache.hadoop.hbase.regionserver.ChunkCreator; 113import org.apache.hadoop.hbase.regionserver.HRegion; 114import org.apache.hadoop.hbase.regionserver.HRegionServer; 115import org.apache.hadoop.hbase.regionserver.HStore; 116import org.apache.hadoop.hbase.regionserver.InternalScanner; 117import org.apache.hadoop.hbase.regionserver.MemStoreLAB; 118import org.apache.hadoop.hbase.regionserver.Region; 119import org.apache.hadoop.hbase.regionserver.RegionScanner; 120import org.apache.hadoop.hbase.regionserver.RegionServerServices; 121import org.apache.hadoop.hbase.regionserver.RegionServerStoppedException; 122import org.apache.hadoop.hbase.security.HBaseKerberosUtils; 123import org.apache.hadoop.hbase.security.User; 124import org.apache.hadoop.hbase.security.visibility.VisibilityLabelsCache; 125import org.apache.hadoop.hbase.util.Bytes; 126import org.apache.hadoop.hbase.util.CommonFSUtils; 127import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 128import org.apache.hadoop.hbase.util.FSTableDescriptors; 129import org.apache.hadoop.hbase.util.FSUtils; 130import org.apache.hadoop.hbase.util.JVM; 131import org.apache.hadoop.hbase.util.JVMClusterUtil; 132import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread; 133import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 134import org.apache.hadoop.hbase.util.Pair; 135import org.apache.hadoop.hbase.util.ReflectionUtils; 136import org.apache.hadoop.hbase.util.RegionSplitter; 137import org.apache.hadoop.hbase.util.RegionSplitter.SplitAlgorithm; 138import org.apache.hadoop.hbase.util.RetryCounter; 139import org.apache.hadoop.hbase.util.Threads; 140import org.apache.hadoop.hbase.wal.WAL; 141import org.apache.hadoop.hbase.wal.WALFactory; 142import org.apache.hadoop.hbase.zookeeper.EmptyWatcher; 143import org.apache.hadoop.hbase.zookeeper.ZKConfig; 144import org.apache.hadoop.hbase.zookeeper.ZKWatcher; 145import org.apache.hadoop.hdfs.DFSClient; 146import org.apache.hadoop.hdfs.DistributedFileSystem; 147import org.apache.hadoop.hdfs.MiniDFSCluster; 148import org.apache.hadoop.hdfs.server.datanode.DataNode; 149import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsDatasetSpi; 150import org.apache.hadoop.hdfs.server.namenode.EditLogFileOutputStream; 151import org.apache.hadoop.mapred.JobConf; 152import org.apache.hadoop.mapred.MiniMRCluster; 153import org.apache.hadoop.mapred.TaskLog; 154import org.apache.hadoop.minikdc.MiniKdc; 155import org.apache.yetus.audience.InterfaceAudience; 156import org.apache.zookeeper.WatchedEvent; 157import org.apache.zookeeper.ZooKeeper; 158import org.apache.zookeeper.ZooKeeper.States; 159 160import org.apache.hbase.thirdparty.com.google.common.io.Closeables; 161 162import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 163 164/** 165 * Facility for testing HBase. Replacement for old HBaseTestCase and HBaseClusterTestCase 166 * functionality. Create an instance and keep it around testing HBase. This class is meant to be 167 * your one-stop shop for anything you might need testing. Manages one cluster at a time only. 168 * Managed cluster can be an in-process {@link MiniHBaseCluster}, or a deployed cluster of type 169 * {@code DistributedHBaseCluster}. Not all methods work with the real cluster. Depends on log4j 170 * being on classpath and hbase-site.xml for logging and test-run configuration. It does not set 171 * logging levels. In the configuration properties, default values for master-info-port and 172 * region-server-port are overridden such that a random port will be assigned (thus avoiding port 173 * contention if another local HBase instance is already running). 174 * <p> 175 * To preserve test data directories, pass the system property "hbase.testing.preserve.testdir" 176 * setting it to true. 177 */ 178@InterfaceAudience.Public 179@SuppressWarnings("deprecation") 180public class HBaseTestingUtility extends HBaseZKTestingUtility { 181 182 /** 183 * System property key to get test directory value. Name is as it is because mini dfs has 184 * hard-codings to put test data here. It should NOT be used directly in HBase, as it's a property 185 * used in mini dfs. 186 * @deprecated since 2.0.0 and will be removed in 3.0.0. Can be used only with mini dfs. 187 * @see <a href="https://issues.apache.org/jira/browse/HBASE-19410">HBASE-19410</a> 188 */ 189 @Deprecated 190 private static final String TEST_DIRECTORY_KEY = "test.build.data"; 191 192 public static final String REGIONS_PER_SERVER_KEY = "hbase.test.regions-per-server"; 193 /** 194 * The default number of regions per regionserver when creating a pre-split table. 195 */ 196 public static final int DEFAULT_REGIONS_PER_SERVER = 3; 197 198 public static final String PRESPLIT_TEST_TABLE_KEY = "hbase.test.pre-split-table"; 199 public static final boolean PRESPLIT_TEST_TABLE = true; 200 201 private MiniDFSCluster dfsCluster = null; 202 private FsDatasetAsyncDiskServiceFixer dfsClusterFixer = null; 203 204 private volatile HBaseCluster hbaseCluster = null; 205 private MiniMRCluster mrCluster = null; 206 207 /** If there is a mini cluster running for this testing utility instance. */ 208 private volatile boolean miniClusterRunning; 209 210 private String hadoopLogDir; 211 212 /** 213 * Directory on test filesystem where we put the data for this instance of HBaseTestingUtility 214 */ 215 private Path dataTestDirOnTestFS = null; 216 217 private final AtomicReference<Connection> connection = new AtomicReference<>(); 218 219 /** Filesystem URI used for map-reduce mini-cluster setup */ 220 private static String FS_URI; 221 222 /** This is for unit tests parameterized with a single boolean. */ 223 public static final List<Object[]> MEMSTORETS_TAGS_PARAMETRIZED = memStoreTSAndTagsCombination(); 224 225 /** 226 * Checks to see if a specific port is available. 227 * @param port the port number to check for availability 228 * @return <tt>true</tt> if the port is available, or <tt>false</tt> if not 229 */ 230 public static boolean available(int port) { 231 ServerSocket ss = null; 232 DatagramSocket ds = null; 233 try { 234 ss = new ServerSocket(port); 235 ss.setReuseAddress(true); 236 ds = new DatagramSocket(port); 237 ds.setReuseAddress(true); 238 return true; 239 } catch (IOException e) { 240 // Do nothing 241 } finally { 242 if (ds != null) { 243 ds.close(); 244 } 245 246 if (ss != null) { 247 try { 248 ss.close(); 249 } catch (IOException e) { 250 /* should not be thrown */ 251 } 252 } 253 } 254 255 return false; 256 } 257 258 /** 259 * Create all combinations of Bloom filters and compression algorithms for testing. 260 */ 261 private static List<Object[]> bloomAndCompressionCombinations() { 262 List<Object[]> configurations = new ArrayList<>(); 263 for (Compression.Algorithm comprAlgo : HBaseCommonTestingUtility.COMPRESSION_ALGORITHMS) { 264 for (BloomType bloomType : BloomType.values()) { 265 configurations.add(new Object[] { comprAlgo, bloomType }); 266 } 267 } 268 return Collections.unmodifiableList(configurations); 269 } 270 271 /** 272 * Create combination of memstoreTS and tags 273 */ 274 private static List<Object[]> memStoreTSAndTagsCombination() { 275 List<Object[]> configurations = new ArrayList<>(); 276 configurations.add(new Object[] { false, false }); 277 configurations.add(new Object[] { false, true }); 278 configurations.add(new Object[] { true, false }); 279 configurations.add(new Object[] { true, true }); 280 return Collections.unmodifiableList(configurations); 281 } 282 283 public static List<Object[]> memStoreTSTagsAndOffheapCombination() { 284 List<Object[]> configurations = new ArrayList<>(); 285 configurations.add(new Object[] { false, false, true }); 286 configurations.add(new Object[] { false, false, false }); 287 configurations.add(new Object[] { false, true, true }); 288 configurations.add(new Object[] { false, true, false }); 289 configurations.add(new Object[] { true, false, true }); 290 configurations.add(new Object[] { true, false, false }); 291 configurations.add(new Object[] { true, true, true }); 292 configurations.add(new Object[] { true, true, false }); 293 return Collections.unmodifiableList(configurations); 294 } 295 296 public static final Collection<Object[]> BLOOM_AND_COMPRESSION_COMBINATIONS = 297 bloomAndCompressionCombinations(); 298 299 /** 300 * <p> 301 * Create an HBaseTestingUtility using a default configuration. 302 * <p> 303 * Initially, all tmp files are written to a local test data directory. Once 304 * {@link #startMiniDFSCluster} is called, either directly or via {@link #startMiniCluster()}, tmp 305 * data will be written to the DFS directory instead. 306 * <p> 307 * Previously, there was a distinction between the type of utility returned by 308 * {@link #createLocalHTU()} and this constructor; this is no longer the case. All 309 * HBaseTestingUtility objects will behave as local until a DFS cluster is started, at which point 310 * they will switch to using mini DFS for storage. 311 */ 312 public HBaseTestingUtility() { 313 this(HBaseConfiguration.create()); 314 } 315 316 /** 317 * <p> 318 * Create an HBaseTestingUtility using a given configuration. 319 * <p> 320 * Initially, all tmp files are written to a local test data directory. Once 321 * {@link #startMiniDFSCluster} is called, either directly or via {@link #startMiniCluster()}, tmp 322 * data will be written to the DFS directory instead. 323 * <p> 324 * Previously, there was a distinction between the type of utility returned by 325 * {@link #createLocalHTU()} and this constructor; this is no longer the case. All 326 * HBaseTestingUtility objects will behave as local until a DFS cluster is started, at which point 327 * they will switch to using mini DFS for storage. 328 * @param conf The configuration to use for further operations 329 */ 330 public HBaseTestingUtility(@Nullable Configuration conf) { 331 super(conf); 332 333 // a hbase checksum verification failure will cause unit tests to fail 334 ChecksumUtil.generateExceptionForChecksumFailureForTest(true); 335 336 // Save this for when setting default file:// breaks things 337 if (this.conf.get("fs.defaultFS") != null) { 338 this.conf.set("original.defaultFS", this.conf.get("fs.defaultFS")); 339 } 340 if (this.conf.get(HConstants.HBASE_DIR) != null) { 341 this.conf.set("original.hbase.dir", this.conf.get(HConstants.HBASE_DIR)); 342 } 343 // Every cluster is a local cluster until we start DFS 344 // Note that conf could be null, but this.conf will not be 345 String dataTestDir = getDataTestDir().toString(); 346 this.conf.set("fs.defaultFS", "file:///"); 347 this.conf.set(HConstants.HBASE_DIR, "file://" + dataTestDir); 348 LOG.debug("Setting {} to {}", HConstants.HBASE_DIR, dataTestDir); 349 this.conf.setBoolean(CommonFSUtils.UNSAFE_STREAM_CAPABILITY_ENFORCE, false); 350 // If the value for random ports isn't set set it to true, thus making 351 // tests opt-out for random port assignment 352 this.conf.setBoolean(LocalHBaseCluster.ASSIGN_RANDOM_PORTS, 353 this.conf.getBoolean(LocalHBaseCluster.ASSIGN_RANDOM_PORTS, true)); 354 } 355 356 /** 357 * @deprecated since 2.0.0 and will be removed in 3.0.0. Use {@link #HBaseTestingUtility()} 358 * instead. 359 * @return a normal HBaseTestingUtility 360 * @see #HBaseTestingUtility() 361 * @see <a href="https://issues.apache.org/jira/browse/HBASE-19841">HBASE-19841</a> 362 */ 363 @Deprecated 364 public static HBaseTestingUtility createLocalHTU() { 365 return new HBaseTestingUtility(); 366 } 367 368 /** 369 * @deprecated since 2.0.0 and will be removed in 3.0.0. Use 370 * {@link #HBaseTestingUtility(Configuration)} instead. 371 * @return a normal HBaseTestingUtility 372 * @see #HBaseTestingUtility(Configuration) 373 * @see <a href="https://issues.apache.org/jira/browse/HBASE-19841">HBASE-19841</a> 374 */ 375 @Deprecated 376 public static HBaseTestingUtility createLocalHTU(Configuration c) { 377 return new HBaseTestingUtility(c); 378 } 379 380 /** 381 * Close both the region {@code r} and it's underlying WAL. For use in tests. 382 */ 383 public static void closeRegionAndWAL(final Region r) throws IOException { 384 closeRegionAndWAL((HRegion) r); 385 } 386 387 /** 388 * Close both the HRegion {@code r} and it's underlying WAL. For use in tests. 389 */ 390 public static void closeRegionAndWAL(final HRegion r) throws IOException { 391 if (r == null) return; 392 r.close(); 393 if (r.getWAL() == null) return; 394 r.getWAL().close(); 395 } 396 397 /** 398 * Returns this classes's instance of {@link Configuration}. Be careful how you use the returned 399 * Configuration since {@link Connection} instances can be shared. The Map of Connections is keyed 400 * by the Configuration. If say, a Connection was being used against a cluster that had been 401 * shutdown, see {@link #shutdownMiniCluster()}, then the Connection will no longer be wholesome. 402 * Rather than use the return direct, its usually best to make a copy and use that. Do 403 * <code>Configuration c = new Configuration(INSTANCE.getConfiguration());</code> 404 * @return Instance of Configuration. 405 */ 406 @Override 407 public Configuration getConfiguration() { 408 return super.getConfiguration(); 409 } 410 411 public void setHBaseCluster(HBaseCluster hbaseCluster) { 412 this.hbaseCluster = hbaseCluster; 413 } 414 415 /** 416 * Home our data in a dir under {@link #DEFAULT_BASE_TEST_DIRECTORY}. Give it a random name so can 417 * have many concurrent tests running if we need to. It needs to amend the 418 * {@link #TEST_DIRECTORY_KEY} System property, as it's what minidfscluster bases it data dir on. 419 * Moding a System property is not the way to do concurrent instances -- another instance could 420 * grab the temporary value unintentionally -- but not anything can do about it at moment; single 421 * instance only is how the minidfscluster works. We also create the underlying directory names 422 * for hadoop.log.dir, mapreduce.cluster.local.dir and hadoop.tmp.dir, and set the values in the 423 * conf, and as a system property for hadoop.tmp.dir (We do not create them!). 424 * @return The calculated data test build directory, if newly-created. 425 */ 426 @Override 427 protected Path setupDataTestDir() { 428 Path testPath = super.setupDataTestDir(); 429 if (null == testPath) { 430 return null; 431 } 432 433 createSubDirAndSystemProperty("hadoop.log.dir", testPath, "hadoop-log-dir"); 434 435 // This is defaulted in core-default.xml to /tmp/hadoop-${user.name}, but 436 // we want our own value to ensure uniqueness on the same machine 437 createSubDirAndSystemProperty("hadoop.tmp.dir", testPath, "hadoop-tmp-dir"); 438 439 // Read and modified in org.apache.hadoop.mapred.MiniMRCluster 440 createSubDir("mapreduce.cluster.local.dir", testPath, "mapred-local-dir"); 441 return testPath; 442 } 443 444 private void createSubDirAndSystemProperty(String propertyName, Path parent, String subDirName) { 445 446 String sysValue = System.getProperty(propertyName); 447 448 if (sysValue != null) { 449 // There is already a value set. So we do nothing but hope 450 // that there will be no conflicts 451 LOG.info("System.getProperty(\"" + propertyName + "\") already set to: " + sysValue 452 + " so I do NOT create it in " + parent); 453 String confValue = conf.get(propertyName); 454 if (confValue != null && !confValue.endsWith(sysValue)) { 455 LOG.warn(propertyName + " property value differs in configuration and system: " 456 + "Configuration=" + confValue + " while System=" + sysValue 457 + " Erasing configuration value by system value."); 458 } 459 conf.set(propertyName, sysValue); 460 } else { 461 // Ok, it's not set, so we create it as a subdirectory 462 createSubDir(propertyName, parent, subDirName); 463 System.setProperty(propertyName, conf.get(propertyName)); 464 } 465 } 466 467 /** 468 * @return Where to write test data on the test filesystem; Returns working directory for the test 469 * filesystem by default 470 * @see #setupDataTestDirOnTestFS() 471 * @see #getTestFileSystem() 472 */ 473 private Path getBaseTestDirOnTestFS() throws IOException { 474 FileSystem fs = getTestFileSystem(); 475 return new Path(fs.getWorkingDirectory(), "test-data"); 476 } 477 478 /** 479 * @return META table descriptor 480 * @deprecated since 2.0 version and will be removed in 3.0 version. Currently for test only. use 481 * {@link #getMetaTableDescriptorBuilder()} 482 */ 483 @Deprecated 484 public HTableDescriptor getMetaTableDescriptor() { 485 return new ImmutableHTableDescriptor(getMetaTableDescriptorBuilder().build()); 486 } 487 488 /** 489 * @return META table descriptor 490 * @deprecated Since 2.3.0. No one should be using this internal. Used in testing only. 491 */ 492 @Deprecated 493 @InterfaceAudience.Private 494 public TableDescriptorBuilder getMetaTableDescriptorBuilder() { 495 try { 496 return FSTableDescriptors.createMetaTableDescriptorBuilder(conf); 497 } catch (IOException e) { 498 throw new RuntimeException("Unable to create META table descriptor", e); 499 } 500 } 501 502 /** 503 * Returns a Path in the test filesystem, obtained from {@link #getTestFileSystem()} to write 504 * temporary test data. Call this method after setting up the mini dfs cluster if the test relies 505 * on it. 506 * @return a unique path in the test filesystem 507 */ 508 public Path getDataTestDirOnTestFS() throws IOException { 509 if (dataTestDirOnTestFS == null) { 510 setupDataTestDirOnTestFS(); 511 } 512 513 return dataTestDirOnTestFS; 514 } 515 516 /** 517 * Returns a Path in the test filesystem, obtained from {@link #getTestFileSystem()} to write 518 * temporary test data. Call this method after setting up the mini dfs cluster if the test relies 519 * on it. 520 * @return a unique path in the test filesystem 521 * @param subdirName name of the subdir to create under the base test dir 522 */ 523 public Path getDataTestDirOnTestFS(final String subdirName) throws IOException { 524 return new Path(getDataTestDirOnTestFS(), subdirName); 525 } 526 527 /** 528 * Sets up a path in test filesystem to be used by tests. Creates a new directory if not already 529 * setup. 530 */ 531 private void setupDataTestDirOnTestFS() throws IOException { 532 if (dataTestDirOnTestFS != null) { 533 LOG.warn("Data test on test fs dir already setup in " + dataTestDirOnTestFS.toString()); 534 return; 535 } 536 dataTestDirOnTestFS = getNewDataTestDirOnTestFS(); 537 } 538 539 /** 540 * Sets up a new path in test filesystem to be used by tests. 541 */ 542 private Path getNewDataTestDirOnTestFS() throws IOException { 543 // The file system can be either local, mini dfs, or if the configuration 544 // is supplied externally, it can be an external cluster FS. If it is a local 545 // file system, the tests should use getBaseTestDir, otherwise, we can use 546 // the working directory, and create a unique sub dir there 547 FileSystem fs = getTestFileSystem(); 548 Path newDataTestDir; 549 String randomStr = getRandomUUID().toString(); 550 if (fs.getUri().getScheme().equals(FileSystem.getLocal(conf).getUri().getScheme())) { 551 newDataTestDir = new Path(getDataTestDir(), randomStr); 552 File dataTestDir = new File(newDataTestDir.toString()); 553 if (deleteOnExit()) dataTestDir.deleteOnExit(); 554 } else { 555 Path base = getBaseTestDirOnTestFS(); 556 newDataTestDir = new Path(base, randomStr); 557 if (deleteOnExit()) fs.deleteOnExit(newDataTestDir); 558 } 559 return newDataTestDir; 560 } 561 562 /** 563 * Cleans the test data directory on the test filesystem. 564 * @return True if we removed the test dirs 565 */ 566 public boolean cleanupDataTestDirOnTestFS() throws IOException { 567 boolean ret = getTestFileSystem().delete(dataTestDirOnTestFS, true); 568 if (ret) dataTestDirOnTestFS = null; 569 return ret; 570 } 571 572 /** 573 * Cleans a subdirectory under the test data directory on the test filesystem. 574 * @return True if we removed child 575 */ 576 public boolean cleanupDataTestDirOnTestFS(String subdirName) throws IOException { 577 Path cpath = getDataTestDirOnTestFS(subdirName); 578 return getTestFileSystem().delete(cpath, true); 579 } 580 581 // Workaround to avoid IllegalThreadStateException 582 // See HBASE-27148 for more details 583 private static final class FsDatasetAsyncDiskServiceFixer extends Thread { 584 585 private volatile boolean stopped = false; 586 587 private final MiniDFSCluster cluster; 588 589 FsDatasetAsyncDiskServiceFixer(MiniDFSCluster cluster) { 590 super("FsDatasetAsyncDiskServiceFixer"); 591 setDaemon(true); 592 this.cluster = cluster; 593 } 594 595 @Override 596 public void run() { 597 while (!stopped) { 598 try { 599 Thread.sleep(30000); 600 } catch (InterruptedException e) { 601 Thread.currentThread().interrupt(); 602 continue; 603 } 604 // we could add new datanodes during tests, so here we will check every 30 seconds, as the 605 // timeout of the thread pool executor is 60 seconds by default. 606 try { 607 for (DataNode dn : cluster.getDataNodes()) { 608 FsDatasetSpi<?> dataset = dn.getFSDataset(); 609 Field service = dataset.getClass().getDeclaredField("asyncDiskService"); 610 service.setAccessible(true); 611 Object asyncDiskService = service.get(dataset); 612 Field group = asyncDiskService.getClass().getDeclaredField("threadGroup"); 613 group.setAccessible(true); 614 ThreadGroup threadGroup = (ThreadGroup) group.get(asyncDiskService); 615 if (threadGroup.isDaemon()) { 616 threadGroup.setDaemon(false); 617 } 618 } 619 } catch (NoSuchFieldException e) { 620 LOG.debug("NoSuchFieldException: " + e.getMessage() 621 + "; It might because your Hadoop version > 3.2.3 or 3.3.4, " 622 + "See HBASE-27595 for details."); 623 } catch (Exception e) { 624 LOG.warn("failed to reset thread pool timeout for FsDatasetAsyncDiskService", e); 625 } 626 } 627 } 628 629 void shutdown() { 630 stopped = true; 631 interrupt(); 632 } 633 } 634 635 /** 636 * Start a minidfscluster. 637 * @param servers How many DNs to start. 638 * @see #shutdownMiniDFSCluster() 639 * @return The mini dfs cluster created. 640 */ 641 public MiniDFSCluster startMiniDFSCluster(int servers) throws Exception { 642 return startMiniDFSCluster(servers, null); 643 } 644 645 /** 646 * Start a minidfscluster. This is useful if you want to run datanode on distinct hosts for things 647 * like HDFS block location verification. If you start MiniDFSCluster without host names, all 648 * instances of the datanodes will have the same host name. 649 * @param hosts hostnames DNs to run on. 650 * @see #shutdownMiniDFSCluster() 651 * @return The mini dfs cluster created. 652 */ 653 public MiniDFSCluster startMiniDFSCluster(final String hosts[]) throws Exception { 654 if (hosts != null && hosts.length != 0) { 655 return startMiniDFSCluster(hosts.length, hosts); 656 } else { 657 return startMiniDFSCluster(1, null); 658 } 659 } 660 661 /** 662 * Start a minidfscluster. Can only create one. 663 * @param servers How many DNs to start. 664 * @param hosts hostnames DNs to run on. 665 * @see #shutdownMiniDFSCluster() 666 * @return The mini dfs cluster created. 667 */ 668 public MiniDFSCluster startMiniDFSCluster(int servers, final String hosts[]) throws Exception { 669 return startMiniDFSCluster(servers, null, hosts); 670 } 671 672 private void setFs() throws IOException { 673 if (this.dfsCluster == null) { 674 LOG.info("Skipping setting fs because dfsCluster is null"); 675 return; 676 } 677 FileSystem fs = this.dfsCluster.getFileSystem(); 678 CommonFSUtils.setFsDefault(this.conf, new Path(fs.getUri())); 679 680 // re-enable this check with dfs 681 conf.unset(CommonFSUtils.UNSAFE_STREAM_CAPABILITY_ENFORCE); 682 } 683 684 public MiniDFSCluster startMiniDFSCluster(int servers, final String racks[], String hosts[]) 685 throws Exception { 686 createDirsAndSetProperties(); 687 EditLogFileOutputStream.setShouldSkipFsyncForTesting(true); 688 689 this.dfsCluster = 690 new MiniDFSCluster(0, this.conf, servers, true, true, true, null, racks, hosts, null); 691 this.dfsClusterFixer = new FsDatasetAsyncDiskServiceFixer(dfsCluster); 692 this.dfsClusterFixer.start(); 693 // Set this just-started cluster as our filesystem. 694 setFs(); 695 696 // Wait for the cluster to be totally up 697 this.dfsCluster.waitClusterUp(); 698 699 // reset the test directory for test file system 700 dataTestDirOnTestFS = null; 701 String dataTestDir = getDataTestDir().toString(); 702 conf.set(HConstants.HBASE_DIR, dataTestDir); 703 LOG.debug("Setting {} to {}", HConstants.HBASE_DIR, dataTestDir); 704 705 return this.dfsCluster; 706 } 707 708 public MiniDFSCluster startMiniDFSClusterForTestWAL(int namenodePort) throws IOException { 709 createDirsAndSetProperties(); 710 dfsCluster = 711 new MiniDFSCluster(namenodePort, conf, 5, false, true, true, null, null, null, null); 712 this.dfsClusterFixer = new FsDatasetAsyncDiskServiceFixer(dfsCluster); 713 this.dfsClusterFixer.start(); 714 return dfsCluster; 715 } 716 717 /** 718 * This is used before starting HDFS and map-reduce mini-clusters Run something like the below to 719 * check for the likes of '/tmp' references -- i.e. references outside of the test data dir -- in 720 * the conf. 721 * 722 * <pre> 723 * Configuration conf = TEST_UTIL.getConfiguration(); 724 * for (Iterator<Map.Entry<String, String>> i = conf.iterator(); i.hasNext();) { 725 * Map.Entry<String, String> e = i.next(); 726 * assertFalse(e.getKey() + " " + e.getValue(), e.getValue().contains("/tmp")); 727 * } 728 * </pre> 729 */ 730 private void createDirsAndSetProperties() throws IOException { 731 setupClusterTestDir(); 732 conf.set(TEST_DIRECTORY_KEY, clusterTestDir.getPath()); 733 System.setProperty(TEST_DIRECTORY_KEY, clusterTestDir.getPath()); 734 createDirAndSetProperty("test.cache.data"); 735 createDirAndSetProperty("hadoop.tmp.dir"); 736 hadoopLogDir = createDirAndSetProperty("hadoop.log.dir"); 737 createDirAndSetProperty("mapreduce.cluster.local.dir"); 738 createDirAndSetProperty("mapreduce.cluster.temp.dir"); 739 enableShortCircuit(); 740 741 Path root = getDataTestDirOnTestFS("hadoop"); 742 conf.set(MapreduceTestingShim.getMROutputDirProp(), 743 new Path(root, "mapred-output-dir").toString()); 744 conf.set("mapreduce.jobtracker.system.dir", new Path(root, "mapred-system-dir").toString()); 745 conf.set("mapreduce.jobtracker.staging.root.dir", 746 new Path(root, "mapreduce-jobtracker-staging-root-dir").toString()); 747 conf.set("mapreduce.job.working.dir", new Path(root, "mapred-working-dir").toString()); 748 conf.set("yarn.app.mapreduce.am.staging-dir", 749 new Path(root, "mapreduce-am-staging-root-dir").toString()); 750 751 // Frustrate yarn's and hdfs's attempts at writing /tmp. 752 // Below is fragile. Make it so we just interpolate any 'tmp' reference. 753 createDirAndSetProperty("yarn.node-labels.fs-store.root-dir"); 754 createDirAndSetProperty("yarn.node-attribute.fs-store.root-dir"); 755 createDirAndSetProperty("yarn.nodemanager.log-dirs"); 756 createDirAndSetProperty("yarn.nodemanager.remote-app-log-dir"); 757 createDirAndSetProperty("yarn.timeline-service.entity-group-fs-store.active-dir"); 758 createDirAndSetProperty("yarn.timeline-service.entity-group-fs-store.done-dir"); 759 createDirAndSetProperty("yarn.nodemanager.remote-app-log-dir"); 760 createDirAndSetProperty("dfs.journalnode.edits.dir"); 761 createDirAndSetProperty("dfs.datanode.shared.file.descriptor.paths"); 762 createDirAndSetProperty("nfs.dump.dir"); 763 createDirAndSetProperty("java.io.tmpdir"); 764 createDirAndSetProperty("dfs.journalnode.edits.dir"); 765 createDirAndSetProperty("dfs.provided.aliasmap.inmemory.leveldb.dir"); 766 createDirAndSetProperty("fs.s3a.committer.staging.tmp.path"); 767 } 768 769 /** 770 * Check whether the tests should assume NEW_VERSION_BEHAVIOR when creating new column families. 771 * Default to false. 772 */ 773 public boolean isNewVersionBehaviorEnabled() { 774 final String propName = "hbase.tests.new.version.behavior"; 775 String v = System.getProperty(propName); 776 if (v != null) { 777 return Boolean.parseBoolean(v); 778 } 779 return false; 780 } 781 782 /** 783 * Get the HBase setting for dfs.client.read.shortcircuit from the conf or a system property. This 784 * allows to specify this parameter on the command line. If not set, default is true. 785 */ 786 public boolean isReadShortCircuitOn() { 787 final String propName = "hbase.tests.use.shortcircuit.reads"; 788 String readOnProp = System.getProperty(propName); 789 if (readOnProp != null) { 790 return Boolean.parseBoolean(readOnProp); 791 } else { 792 return conf.getBoolean(propName, false); 793 } 794 } 795 796 /** 797 * Enable the short circuit read, unless configured differently. Set both HBase and HDFS settings, 798 * including skipping the hdfs checksum checks. 799 */ 800 private void enableShortCircuit() { 801 if (isReadShortCircuitOn()) { 802 String curUser = System.getProperty("user.name"); 803 LOG.info("read short circuit is ON for user " + curUser); 804 // read short circuit, for hdfs 805 conf.set("dfs.block.local-path-access.user", curUser); 806 // read short circuit, for hbase 807 conf.setBoolean("dfs.client.read.shortcircuit", true); 808 // Skip checking checksum, for the hdfs client and the datanode 809 conf.setBoolean("dfs.client.read.shortcircuit.skip.checksum", true); 810 } else { 811 LOG.info("read short circuit is OFF"); 812 } 813 } 814 815 private String createDirAndSetProperty(String property) { 816 return createDirAndSetProperty(property, property); 817 } 818 819 private String createDirAndSetProperty(final String relPath, String property) { 820 String path = getDataTestDir(relPath).toString(); 821 System.setProperty(property, path); 822 conf.set(property, path); 823 new File(path).mkdirs(); 824 LOG.info("Setting " + property + " to " + path + " in system properties and HBase conf"); 825 return path; 826 } 827 828 /** 829 * Shuts down instance created by call to {@link #startMiniDFSCluster(int)} or does nothing. 830 */ 831 public void shutdownMiniDFSCluster() throws IOException { 832 if (this.dfsCluster != null) { 833 // The below throws an exception per dn, AsynchronousCloseException. 834 this.dfsCluster.shutdown(); 835 dfsCluster = null; 836 // It is possible that the dfs cluster is set through setDFSCluster method, where we will not 837 // have a fixer 838 if (dfsClusterFixer != null) { 839 this.dfsClusterFixer.shutdown(); 840 dfsClusterFixer = null; 841 } 842 dataTestDirOnTestFS = null; 843 CommonFSUtils.setFsDefault(this.conf, new Path("file:///")); 844 } 845 } 846 847 /** 848 * Start up a minicluster of hbase, dfs, and zookeeper where WAL's walDir is created separately. 849 * All other options will use default values, defined in {@link StartMiniClusterOption.Builder}. 850 * @param createWALDir Whether to create a new WAL directory. 851 * @return The mini HBase cluster created. 852 * @see #shutdownMiniCluster() 853 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 854 * {@link #startMiniCluster(StartMiniClusterOption)} instead. 855 * @see #startMiniCluster(StartMiniClusterOption) 856 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 857 */ 858 @Deprecated 859 public MiniHBaseCluster startMiniCluster(boolean createWALDir) throws Exception { 860 StartMiniClusterOption option = 861 StartMiniClusterOption.builder().createWALDir(createWALDir).build(); 862 return startMiniCluster(option); 863 } 864 865 /** 866 * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values, 867 * defined in {@link StartMiniClusterOption.Builder}. 868 * @param numSlaves Slave node number, for both HBase region server and HDFS data node. 869 * @param createRootDir Whether to create a new root or data directory path. 870 * @return The mini HBase cluster created. 871 * @see #shutdownMiniCluster() 872 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 873 * {@link #startMiniCluster(StartMiniClusterOption)} instead. 874 * @see #startMiniCluster(StartMiniClusterOption) 875 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 876 */ 877 @Deprecated 878 public MiniHBaseCluster startMiniCluster(int numSlaves, boolean createRootDir) throws Exception { 879 StartMiniClusterOption option = StartMiniClusterOption.builder().numRegionServers(numSlaves) 880 .numDataNodes(numSlaves).createRootDir(createRootDir).build(); 881 return startMiniCluster(option); 882 } 883 884 /** 885 * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values, 886 * defined in {@link StartMiniClusterOption.Builder}. 887 * @param numSlaves Slave node number, for both HBase region server and HDFS data node. 888 * @param createRootDir Whether to create a new root or data directory path. 889 * @param createWALDir Whether to create a new WAL directory. 890 * @return The mini HBase cluster created. 891 * @see #shutdownMiniCluster() 892 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 893 * {@link #startMiniCluster(StartMiniClusterOption)} instead. 894 * @see #startMiniCluster(StartMiniClusterOption) 895 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 896 */ 897 @Deprecated 898 public MiniHBaseCluster startMiniCluster(int numSlaves, boolean createRootDir, 899 boolean createWALDir) throws Exception { 900 StartMiniClusterOption option = StartMiniClusterOption.builder().numRegionServers(numSlaves) 901 .numDataNodes(numSlaves).createRootDir(createRootDir).createWALDir(createWALDir).build(); 902 return startMiniCluster(option); 903 } 904 905 /** 906 * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values, 907 * defined in {@link StartMiniClusterOption.Builder}. 908 * @param numMasters Master node number. 909 * @param numSlaves Slave node number, for both HBase region server and HDFS data node. 910 * @param createRootDir Whether to create a new root or data directory path. 911 * @return The mini HBase cluster created. 912 * @see #shutdownMiniCluster() 913 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 914 * {@link #startMiniCluster(StartMiniClusterOption)} instead. 915 * @see #startMiniCluster(StartMiniClusterOption) 916 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 917 */ 918 @Deprecated 919 public MiniHBaseCluster startMiniCluster(int numMasters, int numSlaves, boolean createRootDir) 920 throws Exception { 921 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters) 922 .numRegionServers(numSlaves).createRootDir(createRootDir).numDataNodes(numSlaves).build(); 923 return startMiniCluster(option); 924 } 925 926 /** 927 * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values, 928 * defined in {@link StartMiniClusterOption.Builder}. 929 * @param numMasters Master node number. 930 * @param numSlaves Slave node number, for both HBase region server and HDFS data node. 931 * @return The mini HBase cluster created. 932 * @see #shutdownMiniCluster() 933 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 934 * {@link #startMiniCluster(StartMiniClusterOption)} instead. 935 * @see #startMiniCluster(StartMiniClusterOption) 936 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 937 */ 938 @Deprecated 939 public MiniHBaseCluster startMiniCluster(int numMasters, int numSlaves) throws Exception { 940 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters) 941 .numRegionServers(numSlaves).numDataNodes(numSlaves).build(); 942 return startMiniCluster(option); 943 } 944 945 /** 946 * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values, 947 * defined in {@link StartMiniClusterOption.Builder}. 948 * @param numMasters Master node number. 949 * @param numSlaves Slave node number, for both HBase region server and HDFS data node. 950 * @param dataNodeHosts The hostnames of DataNodes to run on. If not null, its size will overwrite 951 * HDFS data node number. 952 * @param createRootDir Whether to create a new root or data directory path. 953 * @return The mini HBase cluster created. 954 * @see #shutdownMiniCluster() 955 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 956 * {@link #startMiniCluster(StartMiniClusterOption)} instead. 957 * @see #startMiniCluster(StartMiniClusterOption) 958 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 959 */ 960 @Deprecated 961 public MiniHBaseCluster startMiniCluster(int numMasters, int numSlaves, String[] dataNodeHosts, 962 boolean createRootDir) throws Exception { 963 StartMiniClusterOption option = 964 StartMiniClusterOption.builder().numMasters(numMasters).numRegionServers(numSlaves) 965 .createRootDir(createRootDir).numDataNodes(numSlaves).dataNodeHosts(dataNodeHosts).build(); 966 return startMiniCluster(option); 967 } 968 969 /** 970 * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values, 971 * defined in {@link StartMiniClusterOption.Builder}. 972 * @param numMasters Master node number. 973 * @param numSlaves Slave node number, for both HBase region server and HDFS data node. 974 * @param dataNodeHosts The hostnames of DataNodes to run on. If not null, its size will overwrite 975 * HDFS data node number. 976 * @return The mini HBase cluster created. 977 * @see #shutdownMiniCluster() 978 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 979 * {@link #startMiniCluster(StartMiniClusterOption)} instead. 980 * @see #startMiniCluster(StartMiniClusterOption) 981 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 982 */ 983 @Deprecated 984 public MiniHBaseCluster startMiniCluster(int numMasters, int numSlaves, String[] dataNodeHosts) 985 throws Exception { 986 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters) 987 .numRegionServers(numSlaves).numDataNodes(numSlaves).dataNodeHosts(dataNodeHosts).build(); 988 return startMiniCluster(option); 989 } 990 991 /** 992 * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values, 993 * defined in {@link StartMiniClusterOption.Builder}. 994 * @param numMasters Master node number. 995 * @param numRegionServers Number of region servers. 996 * @param numDataNodes Number of datanodes. 997 * @return The mini HBase cluster created. 998 * @see #shutdownMiniCluster() 999 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 1000 * {@link #startMiniCluster(StartMiniClusterOption)} instead. 1001 * @see #startMiniCluster(StartMiniClusterOption) 1002 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 1003 */ 1004 @Deprecated 1005 public MiniHBaseCluster startMiniCluster(int numMasters, int numRegionServers, int numDataNodes) 1006 throws Exception { 1007 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters) 1008 .numRegionServers(numRegionServers).numDataNodes(numDataNodes).build(); 1009 return startMiniCluster(option); 1010 } 1011 1012 /** 1013 * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values, 1014 * defined in {@link StartMiniClusterOption.Builder}. 1015 * @param numMasters Master node number. 1016 * @param numSlaves Slave node number, for both HBase region server and HDFS data node. 1017 * @param dataNodeHosts The hostnames of DataNodes to run on. If not null, its size will overwrite 1018 * HDFS data node number. 1019 * @param masterClass The class to use as HMaster, or null for default. 1020 * @param rsClass The class to use as HRegionServer, or null for default. 1021 * @return The mini HBase cluster created. 1022 * @see #shutdownMiniCluster() 1023 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 1024 * {@link #startMiniCluster(StartMiniClusterOption)} instead. 1025 * @see #startMiniCluster(StartMiniClusterOption) 1026 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 1027 */ 1028 @Deprecated 1029 public MiniHBaseCluster startMiniCluster(int numMasters, int numSlaves, String[] dataNodeHosts, 1030 Class<? extends HMaster> masterClass, 1031 Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass) throws Exception { 1032 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters) 1033 .masterClass(masterClass).numRegionServers(numSlaves).rsClass(rsClass).numDataNodes(numSlaves) 1034 .dataNodeHosts(dataNodeHosts).build(); 1035 return startMiniCluster(option); 1036 } 1037 1038 /** 1039 * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values, 1040 * defined in {@link StartMiniClusterOption.Builder}. 1041 * @param numMasters Master node number. 1042 * @param numRegionServers Number of region servers. 1043 * @param numDataNodes Number of datanodes. 1044 * @param dataNodeHosts The hostnames of DataNodes to run on. If not null, its size will 1045 * overwrite HDFS data node number. 1046 * @param masterClass The class to use as HMaster, or null for default. 1047 * @param rsClass The class to use as HRegionServer, or null for default. 1048 * @return The mini HBase cluster created. 1049 * @see #shutdownMiniCluster() 1050 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 1051 * {@link #startMiniCluster(StartMiniClusterOption)} instead. 1052 * @see #startMiniCluster(StartMiniClusterOption) 1053 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 1054 */ 1055 @Deprecated 1056 public MiniHBaseCluster startMiniCluster(int numMasters, int numRegionServers, int numDataNodes, 1057 String[] dataNodeHosts, Class<? extends HMaster> masterClass, 1058 Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass) throws Exception { 1059 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters) 1060 .masterClass(masterClass).numRegionServers(numRegionServers).rsClass(rsClass) 1061 .numDataNodes(numDataNodes).dataNodeHosts(dataNodeHosts).build(); 1062 return startMiniCluster(option); 1063 } 1064 1065 /** 1066 * Start up a minicluster of hbase, dfs, and zookeeper. All other options will use default values, 1067 * defined in {@link StartMiniClusterOption.Builder}. 1068 * @param numMasters Master node number. 1069 * @param numRegionServers Number of region servers. 1070 * @param numDataNodes Number of datanodes. 1071 * @param dataNodeHosts The hostnames of DataNodes to run on. If not null, its size will 1072 * overwrite HDFS data node number. 1073 * @param masterClass The class to use as HMaster, or null for default. 1074 * @param rsClass The class to use as HRegionServer, or null for default. 1075 * @param createRootDir Whether to create a new root or data directory path. 1076 * @param createWALDir Whether to create a new WAL directory. 1077 * @return The mini HBase cluster created. 1078 * @see #shutdownMiniCluster() 1079 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 1080 * {@link #startMiniCluster(StartMiniClusterOption)} instead. 1081 * @see #startMiniCluster(StartMiniClusterOption) 1082 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 1083 */ 1084 @Deprecated 1085 public MiniHBaseCluster startMiniCluster(int numMasters, int numRegionServers, int numDataNodes, 1086 String[] dataNodeHosts, Class<? extends HMaster> masterClass, 1087 Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass, boolean createRootDir, 1088 boolean createWALDir) throws Exception { 1089 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters) 1090 .masterClass(masterClass).numRegionServers(numRegionServers).rsClass(rsClass) 1091 .numDataNodes(numDataNodes).dataNodeHosts(dataNodeHosts).createRootDir(createRootDir) 1092 .createWALDir(createWALDir).build(); 1093 return startMiniCluster(option); 1094 } 1095 1096 /** 1097 * Start up a minicluster of hbase, dfs and zookeeper clusters with given slave node number. All 1098 * other options will use default values, defined in {@link StartMiniClusterOption.Builder}. 1099 * @param numSlaves slave node number, for both HBase region server and HDFS data node. 1100 * @see #startMiniCluster(StartMiniClusterOption option) 1101 * @see #shutdownMiniDFSCluster() 1102 */ 1103 public MiniHBaseCluster startMiniCluster(int numSlaves) throws Exception { 1104 StartMiniClusterOption option = 1105 StartMiniClusterOption.builder().numRegionServers(numSlaves).numDataNodes(numSlaves).build(); 1106 return startMiniCluster(option); 1107 } 1108 1109 /** 1110 * Start up a minicluster of hbase, dfs and zookeeper all using default options. Option default 1111 * value can be found in {@link StartMiniClusterOption.Builder}. 1112 * @see #startMiniCluster(StartMiniClusterOption option) 1113 * @see #shutdownMiniDFSCluster() 1114 */ 1115 public MiniHBaseCluster startMiniCluster() throws Exception { 1116 return startMiniCluster(StartMiniClusterOption.builder().build()); 1117 } 1118 1119 /** 1120 * Start up a mini cluster of hbase, optionally dfs and zookeeper if needed. It modifies 1121 * Configuration. It homes the cluster data directory under a random subdirectory in a directory 1122 * under System property test.build.data, to be cleaned up on exit. 1123 * @see #shutdownMiniDFSCluster() 1124 */ 1125 public MiniHBaseCluster startMiniCluster(StartMiniClusterOption option) throws Exception { 1126 LOG.info("Starting up minicluster with option: {}", option); 1127 1128 // If we already put up a cluster, fail. 1129 if (miniClusterRunning) { 1130 throw new IllegalStateException("A mini-cluster is already running"); 1131 } 1132 miniClusterRunning = true; 1133 1134 setupClusterTestDir(); 1135 System.setProperty(TEST_DIRECTORY_KEY, this.clusterTestDir.getPath()); 1136 1137 // Bring up mini dfs cluster. This spews a bunch of warnings about missing 1138 // scheme. Complaints are 'Scheme is undefined for build/test/data/dfs/name1'. 1139 if (dfsCluster == null) { 1140 LOG.info("STARTING DFS"); 1141 dfsCluster = startMiniDFSCluster(option.getNumDataNodes(), option.getDataNodeHosts()); 1142 } else { 1143 LOG.info("NOT STARTING DFS"); 1144 } 1145 1146 // Start up a zk cluster. 1147 if (getZkCluster() == null) { 1148 startMiniZKCluster(option.getNumZkServers()); 1149 } 1150 1151 // Start the MiniHBaseCluster 1152 return startMiniHBaseCluster(option); 1153 } 1154 1155 /** 1156 * Starts up mini hbase cluster. Usually you won't want this. You'll usually want 1157 * {@link #startMiniCluster()}. This is useful when doing stepped startup of clusters. 1158 * @return Reference to the hbase mini hbase cluster. 1159 * @see #startMiniCluster(StartMiniClusterOption) 1160 * @see #shutdownMiniHBaseCluster() 1161 */ 1162 public MiniHBaseCluster startMiniHBaseCluster(StartMiniClusterOption option) 1163 throws IOException, InterruptedException { 1164 // Now do the mini hbase cluster. Set the hbase.rootdir in config. 1165 createRootDir(option.isCreateRootDir()); 1166 if (option.isCreateWALDir()) { 1167 createWALRootDir(); 1168 } 1169 // Set the hbase.fs.tmp.dir config to make sure that we have some default value. This is 1170 // for tests that do not read hbase-defaults.xml 1171 setHBaseFsTmpDir(); 1172 1173 // These settings will make the server waits until this exact number of 1174 // regions servers are connected. 1175 if (conf.getInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, -1) == -1) { 1176 conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, option.getNumRegionServers()); 1177 } 1178 if (conf.getInt(ServerManager.WAIT_ON_REGIONSERVERS_MAXTOSTART, -1) == -1) { 1179 conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MAXTOSTART, option.getNumRegionServers()); 1180 } 1181 1182 Configuration c = new Configuration(this.conf); 1183 this.hbaseCluster = new MiniHBaseCluster(c, option.getNumMasters(), 1184 option.getNumAlwaysStandByMasters(), option.getNumRegionServers(), option.getRsPorts(), 1185 option.getMasterClass(), option.getRsClass()); 1186 // Populate the master address configuration from mini cluster configuration. 1187 conf.set(HConstants.MASTER_ADDRS_KEY, MasterRegistry.getMasterAddr(c)); 1188 // Don't leave here till we've done a successful scan of the hbase:meta 1189 try (Table t = getConnection().getTable(TableName.META_TABLE_NAME); 1190 ResultScanner s = t.getScanner(new Scan())) { 1191 for (;;) { 1192 if (s.next() == null) { 1193 break; 1194 } 1195 } 1196 } 1197 1198 getAdmin(); // create immediately the hbaseAdmin 1199 LOG.info("Minicluster is up; activeMaster={}", getHBaseCluster().getMaster()); 1200 1201 return (MiniHBaseCluster) hbaseCluster; 1202 } 1203 1204 /** 1205 * Starts up mini hbase cluster using default options. Default options can be found in 1206 * {@link StartMiniClusterOption.Builder}. 1207 * @see #startMiniHBaseCluster(StartMiniClusterOption) 1208 * @see #shutdownMiniHBaseCluster() 1209 */ 1210 public MiniHBaseCluster startMiniHBaseCluster() throws IOException, InterruptedException { 1211 return startMiniHBaseCluster(StartMiniClusterOption.builder().build()); 1212 } 1213 1214 /** 1215 * Starts up mini hbase cluster. Usually you won't want this. You'll usually want 1216 * {@link #startMiniCluster()}. All other options will use default values, defined in 1217 * {@link StartMiniClusterOption.Builder}. 1218 * @param numMasters Master node number. 1219 * @param numRegionServers Number of region servers. 1220 * @return The mini HBase cluster created. 1221 * @see #shutdownMiniHBaseCluster() 1222 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 1223 * {@link #startMiniHBaseCluster(StartMiniClusterOption)} instead. 1224 * @see #startMiniHBaseCluster(StartMiniClusterOption) 1225 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 1226 */ 1227 @Deprecated 1228 public MiniHBaseCluster startMiniHBaseCluster(int numMasters, int numRegionServers) 1229 throws IOException, InterruptedException { 1230 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters) 1231 .numRegionServers(numRegionServers).build(); 1232 return startMiniHBaseCluster(option); 1233 } 1234 1235 /** 1236 * Starts up mini hbase cluster. Usually you won't want this. You'll usually want 1237 * {@link #startMiniCluster()}. All other options will use default values, defined in 1238 * {@link StartMiniClusterOption.Builder}. 1239 * @param numMasters Master node number. 1240 * @param numRegionServers Number of region servers. 1241 * @param rsPorts Ports that RegionServer should use. 1242 * @return The mini HBase cluster created. 1243 * @see #shutdownMiniHBaseCluster() 1244 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 1245 * {@link #startMiniHBaseCluster(StartMiniClusterOption)} instead. 1246 * @see #startMiniHBaseCluster(StartMiniClusterOption) 1247 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 1248 */ 1249 @Deprecated 1250 public MiniHBaseCluster startMiniHBaseCluster(int numMasters, int numRegionServers, 1251 List<Integer> rsPorts) throws IOException, InterruptedException { 1252 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters) 1253 .numRegionServers(numRegionServers).rsPorts(rsPorts).build(); 1254 return startMiniHBaseCluster(option); 1255 } 1256 1257 /** 1258 * Starts up mini hbase cluster. Usually you won't want this. You'll usually want 1259 * {@link #startMiniCluster()}. All other options will use default values, defined in 1260 * {@link StartMiniClusterOption.Builder}. 1261 * @param numMasters Master node number. 1262 * @param numRegionServers Number of region servers. 1263 * @param rsPorts Ports that RegionServer should use. 1264 * @param masterClass The class to use as HMaster, or null for default. 1265 * @param rsClass The class to use as HRegionServer, or null for default. 1266 * @param createRootDir Whether to create a new root or data directory path. 1267 * @param createWALDir Whether to create a new WAL directory. 1268 * @return The mini HBase cluster created. 1269 * @see #shutdownMiniHBaseCluster() 1270 * @deprecated since 2.2.0 and will be removed in 4.0.0. Use 1271 * {@link #startMiniHBaseCluster(StartMiniClusterOption)} instead. 1272 * @see #startMiniHBaseCluster(StartMiniClusterOption) 1273 * @see <a href="https://issues.apache.org/jira/browse/HBASE-21071">HBASE-21071</a> 1274 */ 1275 @Deprecated 1276 public MiniHBaseCluster startMiniHBaseCluster(int numMasters, int numRegionServers, 1277 List<Integer> rsPorts, Class<? extends HMaster> masterClass, 1278 Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> rsClass, boolean createRootDir, 1279 boolean createWALDir) throws IOException, InterruptedException { 1280 StartMiniClusterOption option = StartMiniClusterOption.builder().numMasters(numMasters) 1281 .masterClass(masterClass).numRegionServers(numRegionServers).rsClass(rsClass).rsPorts(rsPorts) 1282 .createRootDir(createRootDir).createWALDir(createWALDir).build(); 1283 return startMiniHBaseCluster(option); 1284 } 1285 1286 /** 1287 * Starts the hbase cluster up again after shutting it down previously in a test. Use this if you 1288 * want to keep dfs/zk up and just stop/start hbase. 1289 * @param servers number of region servers 1290 */ 1291 public void restartHBaseCluster(int servers) throws IOException, InterruptedException { 1292 this.restartHBaseCluster(servers, null); 1293 } 1294 1295 public void restartHBaseCluster(int servers, List<Integer> ports) 1296 throws IOException, InterruptedException { 1297 StartMiniClusterOption option = 1298 StartMiniClusterOption.builder().numRegionServers(servers).rsPorts(ports).build(); 1299 restartHBaseCluster(option); 1300 invalidateConnection(); 1301 } 1302 1303 public void restartHBaseCluster(StartMiniClusterOption option) 1304 throws IOException, InterruptedException { 1305 closeConnection(); 1306 this.hbaseCluster = new MiniHBaseCluster(this.conf, option.getNumMasters(), 1307 option.getNumAlwaysStandByMasters(), option.getNumRegionServers(), option.getRsPorts(), 1308 option.getMasterClass(), option.getRsClass()); 1309 // Don't leave here till we've done a successful scan of the hbase:meta 1310 Connection conn = ConnectionFactory.createConnection(this.conf); 1311 Table t = conn.getTable(TableName.META_TABLE_NAME); 1312 ResultScanner s = t.getScanner(new Scan()); 1313 while (s.next() != null) { 1314 // do nothing 1315 } 1316 LOG.info("HBase has been restarted"); 1317 s.close(); 1318 t.close(); 1319 conn.close(); 1320 } 1321 1322 /** 1323 * @return Current mini hbase cluster. Only has something in it after a call to 1324 * {@link #startMiniCluster()}. 1325 * @see #startMiniCluster() 1326 */ 1327 public MiniHBaseCluster getMiniHBaseCluster() { 1328 if (this.hbaseCluster == null || this.hbaseCluster instanceof MiniHBaseCluster) { 1329 return (MiniHBaseCluster) this.hbaseCluster; 1330 } 1331 throw new RuntimeException( 1332 hbaseCluster + " not an instance of " + MiniHBaseCluster.class.getName()); 1333 } 1334 1335 /** 1336 * Stops mini hbase, zk, and hdfs clusters. 1337 * @see #startMiniCluster(int) 1338 */ 1339 public void shutdownMiniCluster() throws IOException { 1340 LOG.info("Shutting down minicluster"); 1341 shutdownMiniHBaseCluster(); 1342 shutdownMiniDFSCluster(); 1343 shutdownMiniZKCluster(); 1344 1345 cleanupTestDir(); 1346 miniClusterRunning = false; 1347 LOG.info("Minicluster is down"); 1348 } 1349 1350 /** 1351 * Shutdown HBase mini cluster.Does not shutdown zk or dfs if running. 1352 * @throws java.io.IOException in case command is unsuccessful 1353 */ 1354 public void shutdownMiniHBaseCluster() throws IOException { 1355 cleanup(); 1356 if (this.hbaseCluster != null) { 1357 this.hbaseCluster.shutdown(); 1358 // Wait till hbase is down before going on to shutdown zk. 1359 this.hbaseCluster.waitUntilShutDown(); 1360 this.hbaseCluster = null; 1361 } 1362 if (zooKeeperWatcher != null) { 1363 zooKeeperWatcher.close(); 1364 zooKeeperWatcher = null; 1365 } 1366 } 1367 1368 /** 1369 * Abruptly Shutdown HBase mini cluster. Does not shutdown zk or dfs if running. 1370 * @throws java.io.IOException throws in case command is unsuccessful 1371 */ 1372 public void killMiniHBaseCluster() throws IOException { 1373 cleanup(); 1374 if (this.hbaseCluster != null) { 1375 getMiniHBaseCluster().killAll(); 1376 this.hbaseCluster = null; 1377 } 1378 if (zooKeeperWatcher != null) { 1379 zooKeeperWatcher.close(); 1380 zooKeeperWatcher = null; 1381 } 1382 } 1383 1384 // close hbase admin, close current connection and reset MIN MAX configs for RS. 1385 private void cleanup() throws IOException { 1386 closeConnection(); 1387 // unset the configuration for MIN and MAX RS to start 1388 conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, -1); 1389 conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MAXTOSTART, -1); 1390 } 1391 1392 /** 1393 * Returns the path to the default root dir the minicluster uses. If <code>create</code> is true, 1394 * a new root directory path is fetched irrespective of whether it has been fetched before or not. 1395 * If false, previous path is used. Note: this does not cause the root dir to be created. 1396 * @return Fully qualified path for the default hbase root dir 1397 */ 1398 public Path getDefaultRootDirPath(boolean create) throws IOException { 1399 if (!create) { 1400 return getDataTestDirOnTestFS(); 1401 } else { 1402 return getNewDataTestDirOnTestFS(); 1403 } 1404 } 1405 1406 /** 1407 * Same as {{@link HBaseTestingUtility#getDefaultRootDirPath(boolean create)} except that 1408 * <code>create</code> flag is false. Note: this does not cause the root dir to be created. 1409 * @return Fully qualified path for the default hbase root dir 1410 */ 1411 public Path getDefaultRootDirPath() throws IOException { 1412 return getDefaultRootDirPath(false); 1413 } 1414 1415 /** 1416 * Creates an hbase rootdir in user home directory. Also creates hbase version file. Normally you 1417 * won't make use of this method. Root hbasedir is created for you as part of mini cluster 1418 * startup. You'd only use this method if you were doing manual operation. 1419 * @param create This flag decides whether to get a new root or data directory path or not, if it 1420 * has been fetched already. Note : Directory will be made irrespective of whether 1421 * path has been fetched or not. If directory already exists, it will be overwritten 1422 * @return Fully qualified path to hbase root dir 1423 */ 1424 public Path createRootDir(boolean create) throws IOException { 1425 FileSystem fs = FileSystem.get(this.conf); 1426 Path hbaseRootdir = getDefaultRootDirPath(create); 1427 CommonFSUtils.setRootDir(this.conf, hbaseRootdir); 1428 fs.mkdirs(hbaseRootdir); 1429 FSUtils.setVersion(fs, hbaseRootdir); 1430 return hbaseRootdir; 1431 } 1432 1433 /** 1434 * Same as {@link HBaseTestingUtility#createRootDir(boolean create)} except that 1435 * <code>create</code> flag is false. 1436 * @return Fully qualified path to hbase root dir 1437 */ 1438 public Path createRootDir() throws IOException { 1439 return createRootDir(false); 1440 } 1441 1442 /** 1443 * Creates a hbase walDir in the user's home directory. Normally you won't make use of this 1444 * method. Root hbaseWALDir is created for you as part of mini cluster startup. You'd only use 1445 * this method if you were doing manual operation. 1446 * @return Fully qualified path to hbase root dir 1447 */ 1448 public Path createWALRootDir() throws IOException { 1449 FileSystem fs = FileSystem.get(this.conf); 1450 Path walDir = getNewDataTestDirOnTestFS(); 1451 CommonFSUtils.setWALRootDir(this.conf, walDir); 1452 fs.mkdirs(walDir); 1453 return walDir; 1454 } 1455 1456 private void setHBaseFsTmpDir() throws IOException { 1457 String hbaseFsTmpDirInString = this.conf.get("hbase.fs.tmp.dir"); 1458 if (hbaseFsTmpDirInString == null) { 1459 this.conf.set("hbase.fs.tmp.dir", getDataTestDirOnTestFS("hbase-staging").toString()); 1460 LOG.info("Setting hbase.fs.tmp.dir to " + this.conf.get("hbase.fs.tmp.dir")); 1461 } else { 1462 LOG.info("The hbase.fs.tmp.dir is set to " + hbaseFsTmpDirInString); 1463 } 1464 } 1465 1466 /** 1467 * Flushes all caches in the mini hbase cluster 1468 */ 1469 public void flush() throws IOException { 1470 getMiniHBaseCluster().flushcache(); 1471 } 1472 1473 /** 1474 * Flushes all caches in the mini hbase cluster 1475 */ 1476 public void flush(TableName tableName) throws IOException { 1477 getMiniHBaseCluster().flushcache(tableName); 1478 } 1479 1480 /** 1481 * Compact all regions in the mini hbase cluster 1482 */ 1483 public void compact(boolean major) throws IOException { 1484 getMiniHBaseCluster().compact(major); 1485 } 1486 1487 /** 1488 * Compact all of a table's reagion in the mini hbase cluster 1489 */ 1490 public void compact(TableName tableName, boolean major) throws IOException { 1491 getMiniHBaseCluster().compact(tableName, major); 1492 } 1493 1494 /** 1495 * Create a table. 1496 * @return A Table instance for the created table. 1497 */ 1498 public Table createTable(TableName tableName, String family) throws IOException { 1499 return createTable(tableName, new String[] { family }); 1500 } 1501 1502 /** 1503 * Create a table. 1504 * @return A Table instance for the created table. 1505 */ 1506 public Table createTable(TableName tableName, String[] families) throws IOException { 1507 List<byte[]> fams = new ArrayList<>(families.length); 1508 for (String family : families) { 1509 fams.add(Bytes.toBytes(family)); 1510 } 1511 return createTable(tableName, fams.toArray(new byte[0][])); 1512 } 1513 1514 /** 1515 * Create a table. 1516 * @return A Table instance for the created table. 1517 */ 1518 public Table createTable(TableName tableName, byte[] family) throws IOException { 1519 return createTable(tableName, new byte[][] { family }); 1520 } 1521 1522 /** 1523 * Create a table with multiple regions. 1524 * @return A Table instance for the created table. 1525 */ 1526 public Table createMultiRegionTable(TableName tableName, byte[] family, int numRegions) 1527 throws IOException { 1528 if (numRegions < 3) throw new IOException("Must create at least 3 regions"); 1529 byte[] startKey = Bytes.toBytes("aaaaa"); 1530 byte[] endKey = Bytes.toBytes("zzzzz"); 1531 byte[][] splitKeys = Bytes.split(startKey, endKey, numRegions - 3); 1532 1533 return createTable(tableName, new byte[][] { family }, splitKeys); 1534 } 1535 1536 /** 1537 * Create a table. 1538 * @return A Table instance for the created table. 1539 */ 1540 public Table createTable(TableName tableName, byte[][] families) throws IOException { 1541 return createTable(tableName, families, (byte[][]) null); 1542 } 1543 1544 /** 1545 * Create a table with multiple regions. 1546 * @return A Table instance for the created table. 1547 */ 1548 public Table createMultiRegionTable(TableName tableName, byte[][] families) throws IOException { 1549 return createTable(tableName, families, KEYS_FOR_HBA_CREATE_TABLE); 1550 } 1551 1552 /** 1553 * Create a table with multiple regions. 1554 * @param replicaCount replica count. 1555 * @return A Table instance for the created table. 1556 */ 1557 public Table createMultiRegionTable(TableName tableName, int replicaCount, byte[][] families) 1558 throws IOException { 1559 return createTable(tableName, families, KEYS_FOR_HBA_CREATE_TABLE, replicaCount); 1560 } 1561 1562 /** 1563 * Create a table. 1564 * @return A Table instance for the created table. 1565 */ 1566 public Table createTable(TableName tableName, byte[][] families, byte[][] splitKeys) 1567 throws IOException { 1568 return createTable(tableName, families, splitKeys, 1, new Configuration(getConfiguration())); 1569 } 1570 1571 /** 1572 * Create a table. 1573 * @param tableName the table name 1574 * @param families the families 1575 * @param splitKeys the splitkeys 1576 * @param replicaCount the region replica count 1577 * @return A Table instance for the created table. 1578 * @throws IOException throws IOException 1579 */ 1580 public Table createTable(TableName tableName, byte[][] families, byte[][] splitKeys, 1581 int replicaCount) throws IOException { 1582 return createTable(tableName, families, splitKeys, replicaCount, 1583 new Configuration(getConfiguration())); 1584 } 1585 1586 public Table createTable(TableName tableName, byte[][] families, int numVersions, byte[] startKey, 1587 byte[] endKey, int numRegions) throws IOException { 1588 HTableDescriptor desc = createTableDescriptor(tableName, families, numVersions); 1589 1590 getAdmin().createTable(desc, startKey, endKey, numRegions); 1591 // HBaseAdmin only waits for regions to appear in hbase:meta we 1592 // should wait until they are assigned 1593 waitUntilAllRegionsAssigned(tableName); 1594 return getConnection().getTable(tableName); 1595 } 1596 1597 /** 1598 * Create a table. 1599 * @param c Configuration to use 1600 * @return A Table instance for the created table. 1601 */ 1602 public Table createTable(TableDescriptor htd, byte[][] families, Configuration c) 1603 throws IOException { 1604 return createTable(htd, families, null, c); 1605 } 1606 1607 /** 1608 * Create a table. 1609 * @param htd table descriptor 1610 * @param families array of column families 1611 * @param splitKeys array of split keys 1612 * @param c Configuration to use 1613 * @return A Table instance for the created table. 1614 * @throws IOException if getAdmin or createTable fails 1615 */ 1616 public Table createTable(TableDescriptor htd, byte[][] families, byte[][] splitKeys, 1617 Configuration c) throws IOException { 1618 // Disable blooms (they are on by default as of 0.95) but we disable them here because 1619 // tests have hard coded counts of what to expect in block cache, etc., and blooms being 1620 // on is interfering. 1621 return createTable(htd, families, splitKeys, BloomType.NONE, HConstants.DEFAULT_BLOCKSIZE, c); 1622 } 1623 1624 /** 1625 * Create a table. 1626 * @param htd table descriptor 1627 * @param families array of column families 1628 * @param splitKeys array of split keys 1629 * @param type Bloom type 1630 * @param blockSize block size 1631 * @param c Configuration to use 1632 * @return A Table instance for the created table. 1633 * @throws IOException if getAdmin or createTable fails 1634 */ 1635 1636 public Table createTable(TableDescriptor htd, byte[][] families, byte[][] splitKeys, 1637 BloomType type, int blockSize, Configuration c) throws IOException { 1638 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(htd); 1639 for (byte[] family : families) { 1640 ColumnFamilyDescriptorBuilder cfdb = ColumnFamilyDescriptorBuilder.newBuilder(family) 1641 .setBloomFilterType(type).setBlocksize(blockSize); 1642 if (isNewVersionBehaviorEnabled()) { 1643 cfdb.setNewVersionBehavior(true); 1644 } 1645 builder.setColumnFamily(cfdb.build()); 1646 } 1647 TableDescriptor td = builder.build(); 1648 getAdmin().createTable(td, splitKeys); 1649 // HBaseAdmin only waits for regions to appear in hbase:meta 1650 // we should wait until they are assigned 1651 waitUntilAllRegionsAssigned(td.getTableName()); 1652 return getConnection().getTable(td.getTableName()); 1653 } 1654 1655 /** 1656 * Create a table. 1657 * @param htd table descriptor 1658 * @param splitRows array of split keys 1659 * @return A Table instance for the created table. 1660 */ 1661 public Table createTable(TableDescriptor htd, byte[][] splitRows) throws IOException { 1662 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(htd); 1663 if (isNewVersionBehaviorEnabled()) { 1664 for (ColumnFamilyDescriptor family : htd.getColumnFamilies()) { 1665 builder.setColumnFamily( 1666 ColumnFamilyDescriptorBuilder.newBuilder(family).setNewVersionBehavior(true).build()); 1667 } 1668 } 1669 getAdmin().createTable(builder.build(), splitRows); 1670 // HBaseAdmin only waits for regions to appear in hbase:meta 1671 // we should wait until they are assigned 1672 waitUntilAllRegionsAssigned(htd.getTableName()); 1673 return getConnection().getTable(htd.getTableName()); 1674 } 1675 1676 /** 1677 * Create a table. 1678 * @param tableName the table name 1679 * @param families the families 1680 * @param splitKeys the split keys 1681 * @param replicaCount the replica count 1682 * @param c Configuration to use 1683 * @return A Table instance for the created table. 1684 */ 1685 public Table createTable(TableName tableName, byte[][] families, byte[][] splitKeys, 1686 int replicaCount, final Configuration c) throws IOException { 1687 HTableDescriptor htd = new HTableDescriptor(tableName); 1688 htd.setRegionReplication(replicaCount); 1689 return createTable(htd, families, splitKeys, c); 1690 } 1691 1692 /** 1693 * Create a table. 1694 * @return A Table instance for the created table. 1695 */ 1696 public Table createTable(TableName tableName, byte[] family, int numVersions) throws IOException { 1697 return createTable(tableName, new byte[][] { family }, numVersions); 1698 } 1699 1700 /** 1701 * Create a table. 1702 * @return A Table instance for the created table. 1703 */ 1704 public Table createTable(TableName tableName, byte[][] families, int numVersions) 1705 throws IOException { 1706 return createTable(tableName, families, numVersions, (byte[][]) null); 1707 } 1708 1709 /** 1710 * Create a table. 1711 * @return A Table instance for the created table. 1712 */ 1713 public Table createTable(TableName tableName, byte[][] families, int numVersions, 1714 byte[][] splitKeys) throws IOException { 1715 HTableDescriptor desc = new HTableDescriptor(tableName); 1716 for (byte[] family : families) { 1717 HColumnDescriptor hcd = new HColumnDescriptor(family).setMaxVersions(numVersions); 1718 if (isNewVersionBehaviorEnabled()) { 1719 hcd.setNewVersionBehavior(true); 1720 } 1721 desc.addFamily(hcd); 1722 } 1723 getAdmin().createTable(desc, splitKeys); 1724 // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are 1725 // assigned 1726 waitUntilAllRegionsAssigned(tableName); 1727 return getConnection().getTable(tableName); 1728 } 1729 1730 /** 1731 * Create a table with multiple regions. 1732 * @return A Table instance for the created table. 1733 */ 1734 public Table createMultiRegionTable(TableName tableName, byte[][] families, int numVersions) 1735 throws IOException { 1736 return createTable(tableName, families, numVersions, KEYS_FOR_HBA_CREATE_TABLE); 1737 } 1738 1739 /** 1740 * Create a table. 1741 * @return A Table instance for the created table. 1742 */ 1743 public Table createTable(TableName tableName, byte[][] families, int numVersions, int blockSize) 1744 throws IOException { 1745 HTableDescriptor desc = new HTableDescriptor(tableName); 1746 for (byte[] family : families) { 1747 HColumnDescriptor hcd = 1748 new HColumnDescriptor(family).setMaxVersions(numVersions).setBlocksize(blockSize); 1749 if (isNewVersionBehaviorEnabled()) { 1750 hcd.setNewVersionBehavior(true); 1751 } 1752 desc.addFamily(hcd); 1753 } 1754 getAdmin().createTable(desc); 1755 // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are 1756 // assigned 1757 waitUntilAllRegionsAssigned(tableName); 1758 return getConnection().getTable(tableName); 1759 } 1760 1761 public Table createTable(TableName tableName, byte[][] families, int numVersions, int blockSize, 1762 String cpName) throws IOException { 1763 HTableDescriptor desc = new HTableDescriptor(tableName); 1764 for (byte[] family : families) { 1765 HColumnDescriptor hcd = 1766 new HColumnDescriptor(family).setMaxVersions(numVersions).setBlocksize(blockSize); 1767 if (isNewVersionBehaviorEnabled()) { 1768 hcd.setNewVersionBehavior(true); 1769 } 1770 desc.addFamily(hcd); 1771 } 1772 if (cpName != null) { 1773 desc.addCoprocessor(cpName); 1774 } 1775 getAdmin().createTable(desc); 1776 // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are 1777 // assigned 1778 waitUntilAllRegionsAssigned(tableName); 1779 return getConnection().getTable(tableName); 1780 } 1781 1782 /** 1783 * Create a table. 1784 * @return A Table instance for the created table. 1785 */ 1786 public Table createTable(TableName tableName, byte[][] families, int[] numVersions) 1787 throws IOException { 1788 HTableDescriptor desc = new HTableDescriptor(tableName); 1789 int i = 0; 1790 for (byte[] family : families) { 1791 HColumnDescriptor hcd = new HColumnDescriptor(family).setMaxVersions(numVersions[i]); 1792 if (isNewVersionBehaviorEnabled()) { 1793 hcd.setNewVersionBehavior(true); 1794 } 1795 desc.addFamily(hcd); 1796 i++; 1797 } 1798 getAdmin().createTable(desc); 1799 // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are 1800 // assigned 1801 waitUntilAllRegionsAssigned(tableName); 1802 return getConnection().getTable(tableName); 1803 } 1804 1805 /** 1806 * Create a table. 1807 * @return A Table instance for the created table. 1808 */ 1809 public Table createTable(TableName tableName, byte[] family, byte[][] splitRows) 1810 throws IOException { 1811 HTableDescriptor desc = new HTableDescriptor(tableName); 1812 HColumnDescriptor hcd = new HColumnDescriptor(family); 1813 if (isNewVersionBehaviorEnabled()) { 1814 hcd.setNewVersionBehavior(true); 1815 } 1816 desc.addFamily(hcd); 1817 getAdmin().createTable(desc, splitRows); 1818 // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are 1819 // assigned 1820 waitUntilAllRegionsAssigned(tableName); 1821 return getConnection().getTable(tableName); 1822 } 1823 1824 /** 1825 * Create a table with multiple regions. 1826 * @return A Table instance for the created table. 1827 */ 1828 public Table createMultiRegionTable(TableName tableName, byte[] family) throws IOException { 1829 return createTable(tableName, family, KEYS_FOR_HBA_CREATE_TABLE); 1830 } 1831 1832 /** 1833 * Modify a table, synchronous. Waiting logic similar to that of {@code admin.rb#alter_status}. 1834 */ 1835 @SuppressWarnings("serial") 1836 public static void modifyTableSync(Admin admin, TableDescriptor desc) 1837 throws IOException, InterruptedException { 1838 admin.modifyTable(desc); 1839 Pair<Integer, Integer> status = new Pair<Integer, Integer>() { 1840 { 1841 setFirst(0); 1842 setSecond(0); 1843 } 1844 }; 1845 int i = 0; 1846 do { 1847 status = admin.getAlterStatus(desc.getTableName()); 1848 if (status.getSecond() != 0) { 1849 LOG.debug( 1850 status.getSecond() - status.getFirst() + "/" + status.getSecond() + " regions updated."); 1851 Thread.sleep(1 * 1000L); 1852 } else { 1853 LOG.debug("All regions updated."); 1854 break; 1855 } 1856 } while (status.getFirst() != 0 && i++ < 500); 1857 if (status.getFirst() != 0) { 1858 throw new IOException("Failed to update all regions even after 500 seconds."); 1859 } 1860 } 1861 1862 /** 1863 * Set the number of Region replicas. 1864 */ 1865 public static void setReplicas(Admin admin, TableName table, int replicaCount) 1866 throws IOException, InterruptedException { 1867 TableDescriptor desc = TableDescriptorBuilder.newBuilder(admin.getDescriptor(table)) 1868 .setRegionReplication(replicaCount).build(); 1869 admin.modifyTable(desc); 1870 } 1871 1872 /** 1873 * Set the number of Region replicas. 1874 */ 1875 public static void setReplicas(AsyncAdmin admin, TableName table, int replicaCount) 1876 throws ExecutionException, IOException, InterruptedException { 1877 TableDescriptor desc = TableDescriptorBuilder.newBuilder(admin.getDescriptor(table).get()) 1878 .setRegionReplication(replicaCount).build(); 1879 admin.modifyTable(desc).get(); 1880 } 1881 1882 /** 1883 * Drop an existing table 1884 * @param tableName existing table 1885 */ 1886 public void deleteTable(TableName tableName) throws IOException { 1887 try { 1888 getAdmin().disableTable(tableName); 1889 } catch (TableNotEnabledException e) { 1890 LOG.debug("Table: " + tableName + " already disabled, so just deleting it."); 1891 } 1892 getAdmin().deleteTable(tableName); 1893 } 1894 1895 /** 1896 * Drop an existing table 1897 * @param tableName existing table 1898 */ 1899 public void deleteTableIfAny(TableName tableName) throws IOException { 1900 try { 1901 deleteTable(tableName); 1902 } catch (TableNotFoundException e) { 1903 // ignore 1904 } 1905 } 1906 1907 // ========================================================================== 1908 // Canned table and table descriptor creation 1909 // TODO replace HBaseTestCase 1910 1911 public final static byte[] fam1 = Bytes.toBytes("colfamily11"); 1912 public final static byte[] fam2 = Bytes.toBytes("colfamily21"); 1913 public final static byte[] fam3 = Bytes.toBytes("colfamily31"); 1914 public static final byte[][] COLUMNS = { fam1, fam2, fam3 }; 1915 private static final int MAXVERSIONS = 3; 1916 1917 public static final char FIRST_CHAR = 'a'; 1918 public static final char LAST_CHAR = 'z'; 1919 public static final byte[] START_KEY_BYTES = { FIRST_CHAR, FIRST_CHAR, FIRST_CHAR }; 1920 public static final String START_KEY = new String(START_KEY_BYTES, HConstants.UTF8_CHARSET); 1921 1922 public TableDescriptorBuilder.ModifyableTableDescriptor 1923 createModifyableTableDescriptor(final String name) { 1924 return createModifyableTableDescriptor(TableName.valueOf(name), 1925 ColumnFamilyDescriptorBuilder.DEFAULT_MIN_VERSIONS, MAXVERSIONS, HConstants.FOREVER, 1926 ColumnFamilyDescriptorBuilder.DEFAULT_KEEP_DELETED); 1927 } 1928 1929 public TableDescriptorBuilder.ModifyableTableDescriptor createModifyableTableDescriptor( 1930 final TableName name, final int minVersions, final int versions, final int ttl, 1931 KeepDeletedCells keepDeleted) { 1932 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(name); 1933 for (byte[] cfName : new byte[][] { fam1, fam2, fam3 }) { 1934 ColumnFamilyDescriptorBuilder cfBuilder = ColumnFamilyDescriptorBuilder.newBuilder(cfName) 1935 .setMinVersions(minVersions).setMaxVersions(versions).setKeepDeletedCells(keepDeleted) 1936 .setBlockCacheEnabled(false).setTimeToLive(ttl); 1937 if (isNewVersionBehaviorEnabled()) { 1938 cfBuilder.setNewVersionBehavior(true); 1939 } 1940 builder.setColumnFamily(cfBuilder.build()); 1941 } 1942 return new TableDescriptorBuilder.ModifyableTableDescriptor(name, builder.build()); 1943 } 1944 1945 /** 1946 * @deprecated since 2.0.0 and will be removed in 3.0.0. Use 1947 * {@link #createTableDescriptor(TableName, int, int, int, KeepDeletedCells)} instead. 1948 * @see #createTableDescriptor(TableName, int, int, int, KeepDeletedCells) 1949 * @see <a href="https://issues.apache.org/jira/browse/HBASE-13893">HBASE-13893</a> 1950 */ 1951 @Deprecated 1952 public HTableDescriptor createTableDescriptor(final String name, final int minVersions, 1953 final int versions, final int ttl, KeepDeletedCells keepDeleted) { 1954 return this.createTableDescriptor(TableName.valueOf(name), minVersions, versions, ttl, 1955 keepDeleted); 1956 } 1957 1958 /** 1959 * Create a table of name <code>name</code>. 1960 * @param name Name to give table. 1961 * @return Column descriptor. 1962 * @deprecated since 2.0.0 and will be removed in 3.0.0. Use 1963 * {@link #createTableDescriptor(TableName, int, int, int, KeepDeletedCells)} instead. 1964 * @see #createTableDescriptor(TableName, int, int, int, KeepDeletedCells) 1965 * @see <a href="https://issues.apache.org/jira/browse/HBASE-13893">HBASE-13893</a> 1966 */ 1967 @Deprecated 1968 public HTableDescriptor createTableDescriptor(final String name) { 1969 return createTableDescriptor(TableName.valueOf(name), HColumnDescriptor.DEFAULT_MIN_VERSIONS, 1970 MAXVERSIONS, HConstants.FOREVER, HColumnDescriptor.DEFAULT_KEEP_DELETED); 1971 } 1972 1973 public HTableDescriptor createTableDescriptor(final TableName name, final int minVersions, 1974 final int versions, final int ttl, KeepDeletedCells keepDeleted) { 1975 HTableDescriptor htd = new HTableDescriptor(name); 1976 for (byte[] cfName : new byte[][] { fam1, fam2, fam3 }) { 1977 HColumnDescriptor hcd = 1978 new HColumnDescriptor(cfName).setMinVersions(minVersions).setMaxVersions(versions) 1979 .setKeepDeletedCells(keepDeleted).setBlockCacheEnabled(false).setTimeToLive(ttl); 1980 if (isNewVersionBehaviorEnabled()) { 1981 hcd.setNewVersionBehavior(true); 1982 } 1983 htd.addFamily(hcd); 1984 } 1985 return htd; 1986 } 1987 1988 /** 1989 * Create a table of name <code>name</code>. 1990 * @param name Name to give table. 1991 * @return Column descriptor. 1992 */ 1993 public HTableDescriptor createTableDescriptor(final TableName name) { 1994 return createTableDescriptor(name, HColumnDescriptor.DEFAULT_MIN_VERSIONS, MAXVERSIONS, 1995 HConstants.FOREVER, HColumnDescriptor.DEFAULT_KEEP_DELETED); 1996 } 1997 1998 public HTableDescriptor createTableDescriptor(final TableName tableName, byte[] family) { 1999 return createTableDescriptor(tableName, new byte[][] { family }, 1); 2000 } 2001 2002 public HTableDescriptor createTableDescriptor(final TableName tableName, byte[][] families, 2003 int maxVersions) { 2004 HTableDescriptor desc = new HTableDescriptor(tableName); 2005 for (byte[] family : families) { 2006 HColumnDescriptor hcd = new HColumnDescriptor(family).setMaxVersions(maxVersions); 2007 if (isNewVersionBehaviorEnabled()) { 2008 hcd.setNewVersionBehavior(true); 2009 } 2010 desc.addFamily(hcd); 2011 } 2012 return desc; 2013 } 2014 2015 /** 2016 * Create an HRegion that writes to the local tmp dirs 2017 * @param desc a table descriptor indicating which table the region belongs to 2018 * @param startKey the start boundary of the region 2019 * @param endKey the end boundary of the region 2020 * @return a region that writes to local dir for testing 2021 */ 2022 public HRegion createLocalHRegion(TableDescriptor desc, byte[] startKey, byte[] endKey) 2023 throws IOException { 2024 HRegionInfo hri = new HRegionInfo(desc.getTableName(), startKey, endKey); 2025 return createLocalHRegion(hri, desc); 2026 } 2027 2028 /** 2029 * Create an HRegion that writes to the local tmp dirs. Creates the WAL for you. Be sure to call 2030 * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} when you're finished with it. 2031 */ 2032 public HRegion createLocalHRegion(RegionInfo info, TableDescriptor desc) throws IOException { 2033 return createRegionAndWAL(info, getDataTestDir(), getConfiguration(), desc); 2034 } 2035 2036 /** 2037 * Create an HRegion that writes to the local tmp dirs with specified wal 2038 * @param info regioninfo 2039 * @param conf configuration 2040 * @param desc table descriptor 2041 * @param wal wal for this region. 2042 * @return created hregion 2043 */ 2044 public HRegion createLocalHRegion(RegionInfo info, Configuration conf, TableDescriptor desc, 2045 WAL wal) throws IOException { 2046 ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null, 2047 MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 2048 return HRegion.createHRegion(info, getDataTestDir(), conf, desc, wal); 2049 } 2050 2051 /** 2052 * Create an HRegion that writes to the local tmp dirs with specified wal 2053 * @param info regioninfo 2054 * @param info configuration 2055 * @param desc table descriptor 2056 * @param wal wal for this region. 2057 * @return created hregion 2058 */ 2059 public HRegion createLocalHRegion(HRegionInfo info, Configuration conf, HTableDescriptor desc, 2060 WAL wal) throws IOException { 2061 ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null, 2062 MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 2063 return HRegion.createHRegion(info, getDataTestDir(), conf, desc, wal); 2064 } 2065 2066 /** 2067 * @param tableName the name of the table 2068 * @param startKey the start key of the region 2069 * @param stopKey the stop key of the region 2070 * @param callingMethod the name of the calling method probably a test method 2071 * @param conf the configuration to use 2072 * @param isReadOnly {@code true} if the table is read only, {@code false} otherwise 2073 * @param families the column families to use 2074 * @throws IOException if an IO problem is encountered 2075 * @return A region on which you must call {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} 2076 * when done. 2077 * @deprecated since 2.0.0 and will be removed in 3.0.0. Use {@link #createLocalHRegion(TableName, 2078 * byte[], byte[], boolean, Durability, WAL, byte[]...)} instead. 2079 * @see #createLocalHRegion(TableName, byte[], byte[], boolean, Durability, WAL, byte[]...) 2080 * @see <a href="https://issues.apache.org/jira/browse/HBASE-13893">HBASE-13893</a> 2081 */ 2082 @Deprecated 2083 public HRegion createLocalHRegion(byte[] tableName, byte[] startKey, byte[] stopKey, 2084 String callingMethod, Configuration conf, boolean isReadOnly, Durability durability, WAL wal, 2085 byte[]... families) throws IOException { 2086 return createLocalHRegion(TableName.valueOf(tableName), startKey, stopKey, conf, isReadOnly, 2087 durability, wal, families); 2088 } 2089 2090 /** 2091 * Return a region on which you must call {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} 2092 * when done. 2093 */ 2094 public HRegion createLocalHRegion(TableName tableName, byte[] startKey, byte[] stopKey, 2095 Configuration conf, boolean isReadOnly, Durability durability, WAL wal, byte[]... families) 2096 throws IOException { 2097 return createLocalHRegionWithInMemoryFlags(tableName, startKey, stopKey, conf, isReadOnly, 2098 durability, wal, null, families); 2099 } 2100 2101 public HRegion createLocalHRegionWithInMemoryFlags(TableName tableName, byte[] startKey, 2102 byte[] stopKey, Configuration conf, boolean isReadOnly, Durability durability, WAL wal, 2103 boolean[] compactedMemStore, byte[]... families) throws IOException { 2104 HTableDescriptor htd = new HTableDescriptor(tableName); 2105 htd.setReadOnly(isReadOnly); 2106 int i = 0; 2107 for (byte[] family : families) { 2108 HColumnDescriptor hcd = new HColumnDescriptor(family); 2109 if (compactedMemStore != null && i < compactedMemStore.length) { 2110 hcd.setInMemoryCompaction(MemoryCompactionPolicy.BASIC); 2111 } else { 2112 hcd.setInMemoryCompaction(MemoryCompactionPolicy.NONE); 2113 2114 } 2115 i++; 2116 // Set default to be three versions. 2117 hcd.setMaxVersions(Integer.MAX_VALUE); 2118 htd.addFamily(hcd); 2119 } 2120 htd.setDurability(durability); 2121 HRegionInfo info = new HRegionInfo(htd.getTableName(), startKey, stopKey, false); 2122 return createLocalHRegion(info, conf, htd, wal); 2123 } 2124 2125 // 2126 // ========================================================================== 2127 2128 /** 2129 * Provide an existing table name to truncate. Scans the table and issues a delete for each row 2130 * read. 2131 * @param tableName existing table 2132 * @return HTable to that new table 2133 */ 2134 public Table deleteTableData(TableName tableName) throws IOException { 2135 Table table = getConnection().getTable(tableName); 2136 Scan scan = new Scan(); 2137 ResultScanner resScan = table.getScanner(scan); 2138 for (Result res : resScan) { 2139 Delete del = new Delete(res.getRow()); 2140 table.delete(del); 2141 } 2142 resScan = table.getScanner(scan); 2143 resScan.close(); 2144 return table; 2145 } 2146 2147 /** 2148 * Truncate a table using the admin command. Effectively disables, deletes, and recreates the 2149 * table. 2150 * @param tableName table which must exist. 2151 * @param preserveRegions keep the existing split points 2152 * @return HTable for the new table 2153 */ 2154 public Table truncateTable(final TableName tableName, final boolean preserveRegions) 2155 throws IOException { 2156 Admin admin = getAdmin(); 2157 if (!admin.isTableDisabled(tableName)) { 2158 admin.disableTable(tableName); 2159 } 2160 admin.truncateTable(tableName, preserveRegions); 2161 return getConnection().getTable(tableName); 2162 } 2163 2164 /** 2165 * Truncate a table using the admin command. Effectively disables, deletes, and recreates the 2166 * table. For previous behavior of issuing row deletes, see deleteTableData. Expressly does not 2167 * preserve regions of existing table. 2168 * @param tableName table which must exist. 2169 * @return HTable for the new table 2170 */ 2171 public Table truncateTable(final TableName tableName) throws IOException { 2172 return truncateTable(tableName, false); 2173 } 2174 2175 /** 2176 * Load table with rows from 'aaa' to 'zzz'. 2177 * @param t Table 2178 * @param f Family 2179 * @return Count of rows loaded. 2180 */ 2181 public int loadTable(final Table t, final byte[] f) throws IOException { 2182 return loadTable(t, new byte[][] { f }); 2183 } 2184 2185 /** 2186 * Load table with rows from 'aaa' to 'zzz'. 2187 * @param t Table 2188 * @param f Family 2189 * @return Count of rows loaded. 2190 */ 2191 public int loadTable(final Table t, final byte[] f, boolean writeToWAL) throws IOException { 2192 return loadTable(t, new byte[][] { f }, null, writeToWAL); 2193 } 2194 2195 /** 2196 * Load table of multiple column families with rows from 'aaa' to 'zzz'. 2197 * @param t Table 2198 * @param f Array of Families to load 2199 * @return Count of rows loaded. 2200 */ 2201 public int loadTable(final Table t, final byte[][] f) throws IOException { 2202 return loadTable(t, f, null); 2203 } 2204 2205 /** 2206 * Load table of multiple column families with rows from 'aaa' to 'zzz'. 2207 * @param t Table 2208 * @param f Array of Families to load 2209 * @param value the values of the cells. If null is passed, the row key is used as value 2210 * @return Count of rows loaded. 2211 */ 2212 public int loadTable(final Table t, final byte[][] f, byte[] value) throws IOException { 2213 return loadTable(t, f, value, true); 2214 } 2215 2216 /** 2217 * Load table of multiple column families with rows from 'aaa' to 'zzz'. 2218 * @param t Table 2219 * @param f Array of Families to load 2220 * @param value the values of the cells. If null is passed, the row key is used as value 2221 * @return Count of rows loaded. 2222 */ 2223 public int loadTable(final Table t, final byte[][] f, byte[] value, boolean writeToWAL) 2224 throws IOException { 2225 List<Put> puts = new ArrayList<>(); 2226 for (byte[] row : HBaseTestingUtility.ROWS) { 2227 Put put = new Put(row); 2228 put.setDurability(writeToWAL ? Durability.USE_DEFAULT : Durability.SKIP_WAL); 2229 for (int i = 0; i < f.length; i++) { 2230 byte[] value1 = value != null ? value : row; 2231 put.addColumn(f[i], f[i], value1); 2232 } 2233 puts.add(put); 2234 } 2235 t.put(puts); 2236 return puts.size(); 2237 } 2238 2239 /** 2240 * A tracker for tracking and validating table rows generated with 2241 * {@link HBaseTestingUtility#loadTable(Table, byte[])} 2242 */ 2243 public static class SeenRowTracker { 2244 int dim = 'z' - 'a' + 1; 2245 int[][][] seenRows = new int[dim][dim][dim]; // count of how many times the row is seen 2246 byte[] startRow; 2247 byte[] stopRow; 2248 2249 public SeenRowTracker(byte[] startRow, byte[] stopRow) { 2250 this.startRow = startRow; 2251 this.stopRow = stopRow; 2252 } 2253 2254 void reset() { 2255 for (byte[] row : ROWS) { 2256 seenRows[i(row[0])][i(row[1])][i(row[2])] = 0; 2257 } 2258 } 2259 2260 int i(byte b) { 2261 return b - 'a'; 2262 } 2263 2264 public void addRow(byte[] row) { 2265 seenRows[i(row[0])][i(row[1])][i(row[2])]++; 2266 } 2267 2268 /** 2269 * Validate that all the rows between startRow and stopRow are seen exactly once, and all other 2270 * rows none 2271 */ 2272 public void validate() { 2273 for (byte b1 = 'a'; b1 <= 'z'; b1++) { 2274 for (byte b2 = 'a'; b2 <= 'z'; b2++) { 2275 for (byte b3 = 'a'; b3 <= 'z'; b3++) { 2276 int count = seenRows[i(b1)][i(b2)][i(b3)]; 2277 int expectedCount = 0; 2278 if ( 2279 Bytes.compareTo(new byte[] { b1, b2, b3 }, startRow) >= 0 2280 && Bytes.compareTo(new byte[] { b1, b2, b3 }, stopRow) < 0 2281 ) { 2282 expectedCount = 1; 2283 } 2284 if (count != expectedCount) { 2285 String row = new String(new byte[] { b1, b2, b3 }, StandardCharsets.UTF_8); 2286 throw new RuntimeException("Row:" + row + " has a seen count of " + count + " " 2287 + "instead of " + expectedCount); 2288 } 2289 } 2290 } 2291 } 2292 } 2293 } 2294 2295 public int loadRegion(final HRegion r, final byte[] f) throws IOException { 2296 return loadRegion(r, f, false); 2297 } 2298 2299 public int loadRegion(final Region r, final byte[] f) throws IOException { 2300 return loadRegion((HRegion) r, f); 2301 } 2302 2303 /** 2304 * Load region with rows from 'aaa' to 'zzz'. 2305 * @param r Region 2306 * @param f Family 2307 * @param flush flush the cache if true 2308 * @return Count of rows loaded. 2309 */ 2310 public int loadRegion(final HRegion r, final byte[] f, final boolean flush) throws IOException { 2311 byte[] k = new byte[3]; 2312 int rowCount = 0; 2313 for (byte b1 = 'a'; b1 <= 'z'; b1++) { 2314 for (byte b2 = 'a'; b2 <= 'z'; b2++) { 2315 for (byte b3 = 'a'; b3 <= 'z'; b3++) { 2316 k[0] = b1; 2317 k[1] = b2; 2318 k[2] = b3; 2319 Put put = new Put(k); 2320 put.setDurability(Durability.SKIP_WAL); 2321 put.addColumn(f, null, k); 2322 if (r.getWAL() == null) { 2323 put.setDurability(Durability.SKIP_WAL); 2324 } 2325 int preRowCount = rowCount; 2326 int pause = 10; 2327 int maxPause = 1000; 2328 while (rowCount == preRowCount) { 2329 try { 2330 r.put(put); 2331 rowCount++; 2332 } catch (RegionTooBusyException e) { 2333 pause = (pause * 2 >= maxPause) ? maxPause : pause * 2; 2334 Threads.sleep(pause); 2335 } 2336 } 2337 } 2338 } 2339 if (flush) { 2340 r.flush(true); 2341 } 2342 } 2343 return rowCount; 2344 } 2345 2346 public void loadNumericRows(final Table t, final byte[] f, int startRow, int endRow) 2347 throws IOException { 2348 for (int i = startRow; i < endRow; i++) { 2349 byte[] data = Bytes.toBytes(String.valueOf(i)); 2350 Put put = new Put(data); 2351 put.addColumn(f, null, data); 2352 t.put(put); 2353 } 2354 } 2355 2356 public void loadRandomRows(final Table t, final byte[] f, int rowSize, int totalRows) 2357 throws IOException { 2358 byte[] row = new byte[rowSize]; 2359 for (int i = 0; i < totalRows; i++) { 2360 Bytes.random(row); 2361 Put put = new Put(row); 2362 put.addColumn(f, new byte[] { 0 }, new byte[] { 0 }); 2363 t.put(put); 2364 } 2365 } 2366 2367 public void verifyNumericRows(Table table, final byte[] f, int startRow, int endRow, 2368 int replicaId) throws IOException { 2369 for (int i = startRow; i < endRow; i++) { 2370 String failMsg = "Failed verification of row :" + i; 2371 byte[] data = Bytes.toBytes(String.valueOf(i)); 2372 Get get = new Get(data); 2373 get.setReplicaId(replicaId); 2374 get.setConsistency(Consistency.TIMELINE); 2375 Result result = table.get(get); 2376 assertTrue(failMsg, result.containsColumn(f, null)); 2377 assertEquals(failMsg, 1, result.getColumnCells(f, null).size()); 2378 Cell cell = result.getColumnLatestCell(f, null); 2379 assertTrue(failMsg, Bytes.equals(data, 0, data.length, cell.getValueArray(), 2380 cell.getValueOffset(), cell.getValueLength())); 2381 } 2382 } 2383 2384 public void verifyNumericRows(Region region, final byte[] f, int startRow, int endRow) 2385 throws IOException { 2386 verifyNumericRows((HRegion) region, f, startRow, endRow); 2387 } 2388 2389 public void verifyNumericRows(HRegion region, final byte[] f, int startRow, int endRow) 2390 throws IOException { 2391 verifyNumericRows(region, f, startRow, endRow, true); 2392 } 2393 2394 public void verifyNumericRows(Region region, final byte[] f, int startRow, int endRow, 2395 final boolean present) throws IOException { 2396 verifyNumericRows((HRegion) region, f, startRow, endRow, present); 2397 } 2398 2399 public void verifyNumericRows(HRegion region, final byte[] f, int startRow, int endRow, 2400 final boolean present) throws IOException { 2401 for (int i = startRow; i < endRow; i++) { 2402 String failMsg = "Failed verification of row :" + i; 2403 byte[] data = Bytes.toBytes(String.valueOf(i)); 2404 Result result = region.get(new Get(data)); 2405 2406 boolean hasResult = result != null && !result.isEmpty(); 2407 assertEquals(failMsg + result, present, hasResult); 2408 if (!present) continue; 2409 2410 assertTrue(failMsg, result.containsColumn(f, null)); 2411 assertEquals(failMsg, 1, result.getColumnCells(f, null).size()); 2412 Cell cell = result.getColumnLatestCell(f, null); 2413 assertTrue(failMsg, Bytes.equals(data, 0, data.length, cell.getValueArray(), 2414 cell.getValueOffset(), cell.getValueLength())); 2415 } 2416 } 2417 2418 public void deleteNumericRows(final Table t, final byte[] f, int startRow, int endRow) 2419 throws IOException { 2420 for (int i = startRow; i < endRow; i++) { 2421 byte[] data = Bytes.toBytes(String.valueOf(i)); 2422 Delete delete = new Delete(data); 2423 delete.addFamily(f); 2424 t.delete(delete); 2425 } 2426 } 2427 2428 /** 2429 * Return the number of rows in the given table. 2430 * @param table to count rows 2431 * @return count of rows 2432 */ 2433 public int countRows(final Table table) throws IOException { 2434 return countRows(table, new Scan()); 2435 } 2436 2437 public int countRows(final Table table, final Scan scan) throws IOException { 2438 try (ResultScanner results = table.getScanner(scan)) { 2439 int count = 0; 2440 while (results.next() != null) { 2441 count++; 2442 } 2443 return count; 2444 } 2445 } 2446 2447 public int countRows(final Table table, final byte[]... families) throws IOException { 2448 Scan scan = new Scan(); 2449 for (byte[] family : families) { 2450 scan.addFamily(family); 2451 } 2452 return countRows(table, scan); 2453 } 2454 2455 /** 2456 * Return the number of rows in the given table. 2457 */ 2458 public int countRows(final TableName tableName) throws IOException { 2459 Table table = getConnection().getTable(tableName); 2460 try { 2461 return countRows(table); 2462 } finally { 2463 table.close(); 2464 } 2465 } 2466 2467 public int countRows(final Region region) throws IOException { 2468 return countRows(region, new Scan()); 2469 } 2470 2471 public int countRows(final Region region, final Scan scan) throws IOException { 2472 InternalScanner scanner = region.getScanner(scan); 2473 try { 2474 return countRows(scanner); 2475 } finally { 2476 scanner.close(); 2477 } 2478 } 2479 2480 public int countRows(final InternalScanner scanner) throws IOException { 2481 int scannedCount = 0; 2482 List<Cell> results = new ArrayList<>(); 2483 boolean hasMore = true; 2484 while (hasMore) { 2485 hasMore = scanner.next(results); 2486 scannedCount += results.size(); 2487 results.clear(); 2488 } 2489 return scannedCount; 2490 } 2491 2492 /** 2493 * Return an md5 digest of the entire contents of a table. 2494 */ 2495 public String checksumRows(final Table table) throws Exception { 2496 2497 Scan scan = new Scan(); 2498 ResultScanner results = table.getScanner(scan); 2499 MessageDigest digest = MessageDigest.getInstance("MD5"); 2500 for (Result res : results) { 2501 digest.update(res.getRow()); 2502 } 2503 results.close(); 2504 return digest.toString(); 2505 } 2506 2507 /** All the row values for the data loaded by {@link #loadTable(Table, byte[])} */ 2508 public static final byte[][] ROWS = new byte[(int) Math.pow('z' - 'a' + 1, 3)][3]; // ~52KB 2509 static { 2510 int i = 0; 2511 for (byte b1 = 'a'; b1 <= 'z'; b1++) { 2512 for (byte b2 = 'a'; b2 <= 'z'; b2++) { 2513 for (byte b3 = 'a'; b3 <= 'z'; b3++) { 2514 ROWS[i][0] = b1; 2515 ROWS[i][1] = b2; 2516 ROWS[i][2] = b3; 2517 i++; 2518 } 2519 } 2520 } 2521 } 2522 2523 public static final byte[][] KEYS = { HConstants.EMPTY_BYTE_ARRAY, Bytes.toBytes("bbb"), 2524 Bytes.toBytes("ccc"), Bytes.toBytes("ddd"), Bytes.toBytes("eee"), Bytes.toBytes("fff"), 2525 Bytes.toBytes("ggg"), Bytes.toBytes("hhh"), Bytes.toBytes("iii"), Bytes.toBytes("jjj"), 2526 Bytes.toBytes("kkk"), Bytes.toBytes("lll"), Bytes.toBytes("mmm"), Bytes.toBytes("nnn"), 2527 Bytes.toBytes("ooo"), Bytes.toBytes("ppp"), Bytes.toBytes("qqq"), Bytes.toBytes("rrr"), 2528 Bytes.toBytes("sss"), Bytes.toBytes("ttt"), Bytes.toBytes("uuu"), Bytes.toBytes("vvv"), 2529 Bytes.toBytes("www"), Bytes.toBytes("xxx"), Bytes.toBytes("yyy") }; 2530 2531 public static final byte[][] KEYS_FOR_HBA_CREATE_TABLE = { Bytes.toBytes("bbb"), 2532 Bytes.toBytes("ccc"), Bytes.toBytes("ddd"), Bytes.toBytes("eee"), Bytes.toBytes("fff"), 2533 Bytes.toBytes("ggg"), Bytes.toBytes("hhh"), Bytes.toBytes("iii"), Bytes.toBytes("jjj"), 2534 Bytes.toBytes("kkk"), Bytes.toBytes("lll"), Bytes.toBytes("mmm"), Bytes.toBytes("nnn"), 2535 Bytes.toBytes("ooo"), Bytes.toBytes("ppp"), Bytes.toBytes("qqq"), Bytes.toBytes("rrr"), 2536 Bytes.toBytes("sss"), Bytes.toBytes("ttt"), Bytes.toBytes("uuu"), Bytes.toBytes("vvv"), 2537 Bytes.toBytes("www"), Bytes.toBytes("xxx"), Bytes.toBytes("yyy"), Bytes.toBytes("zzz") }; 2538 2539 /** 2540 * Create rows in hbase:meta for regions of the specified table with the specified start keys. The 2541 * first startKey should be a 0 length byte array if you want to form a proper range of regions. 2542 * @return list of region info for regions added to meta 2543 * @deprecated since 2.0 version and will be removed in 3.0 version. use 2544 * {@link #createMultiRegionsInMeta(Configuration, TableDescriptor, byte[][])} 2545 */ 2546 @Deprecated 2547 public List<HRegionInfo> createMultiRegionsInMeta(final Configuration conf, 2548 final HTableDescriptor htd, byte[][] startKeys) throws IOException { 2549 return createMultiRegionsInMeta(conf, (TableDescriptor) htd, startKeys).stream() 2550 .map(ImmutableHRegionInfo::new).collect(Collectors.toList()); 2551 } 2552 2553 /** 2554 * Create rows in hbase:meta for regions of the specified table with the specified start keys. The 2555 * first startKey should be a 0 length byte array if you want to form a proper range of regions. 2556 * @return list of region info for regions added to meta 2557 */ 2558 public List<RegionInfo> createMultiRegionsInMeta(final Configuration conf, 2559 final TableDescriptor htd, byte[][] startKeys) throws IOException { 2560 Table meta = getConnection().getTable(TableName.META_TABLE_NAME); 2561 Arrays.sort(startKeys, Bytes.BYTES_COMPARATOR); 2562 List<RegionInfo> newRegions = new ArrayList<>(startKeys.length); 2563 MetaTableAccessor.updateTableState(getConnection(), htd.getTableName(), 2564 TableState.State.ENABLED); 2565 // add custom ones 2566 for (int i = 0; i < startKeys.length; i++) { 2567 int j = (i + 1) % startKeys.length; 2568 RegionInfo hri = RegionInfoBuilder.newBuilder(htd.getTableName()).setStartKey(startKeys[i]) 2569 .setEndKey(startKeys[j]).build(); 2570 MetaTableAccessor.addRegionsToMeta(getConnection(), Collections.singletonList(hri), 1); 2571 newRegions.add(hri); 2572 } 2573 2574 meta.close(); 2575 return newRegions; 2576 } 2577 2578 /** 2579 * Create an unmanaged WAL. Be sure to close it when you're through. 2580 */ 2581 public static WAL createWal(final Configuration conf, final Path rootDir, final RegionInfo hri) 2582 throws IOException { 2583 // The WAL subsystem will use the default rootDir rather than the passed in rootDir 2584 // unless I pass along via the conf. 2585 Configuration confForWAL = new Configuration(conf); 2586 confForWAL.set(HConstants.HBASE_DIR, rootDir.toString()); 2587 return new WALFactory(confForWAL, "hregion-" + RandomStringUtils.randomNumeric(8)).getWAL(hri); 2588 } 2589 2590 /** 2591 * Create a region with it's own WAL. Be sure to call 2592 * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to clean up all resources. 2593 */ 2594 public static HRegion createRegionAndWAL(final RegionInfo info, final Path rootDir, 2595 final Configuration conf, final TableDescriptor htd) throws IOException { 2596 return createRegionAndWAL(info, rootDir, conf, htd, true); 2597 } 2598 2599 /** 2600 * Create a region with it's own WAL. Be sure to call 2601 * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to clean up all resources. 2602 */ 2603 public static HRegion createRegionAndWAL(final RegionInfo info, final Path rootDir, 2604 final Configuration conf, final TableDescriptor htd, BlockCache blockCache) throws IOException { 2605 HRegion region = createRegionAndWAL(info, rootDir, conf, htd, false); 2606 region.setBlockCache(blockCache); 2607 region.initialize(); 2608 return region; 2609 } 2610 2611 /** 2612 * Create a region with it's own WAL. Be sure to call 2613 * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to clean up all resources. 2614 */ 2615 public static HRegion createRegionAndWAL(final RegionInfo info, final Path rootDir, 2616 final Configuration conf, final TableDescriptor htd, MobFileCache mobFileCache) 2617 throws IOException { 2618 HRegion region = createRegionAndWAL(info, rootDir, conf, htd, false); 2619 region.setMobFileCache(mobFileCache); 2620 region.initialize(); 2621 return region; 2622 } 2623 2624 /** 2625 * Create a region with it's own WAL. Be sure to call 2626 * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to clean up all resources. 2627 */ 2628 public static HRegion createRegionAndWAL(final RegionInfo info, final Path rootDir, 2629 final Configuration conf, final TableDescriptor htd, boolean initialize) throws IOException { 2630 ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null, 2631 MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 2632 WAL wal = createWal(conf, rootDir, info); 2633 return HRegion.createHRegion(info, rootDir, conf, htd, wal, initialize); 2634 } 2635 2636 /** 2637 * Returns all rows from the hbase:meta table. 2638 * @throws IOException When reading the rows fails. 2639 */ 2640 public List<byte[]> getMetaTableRows() throws IOException { 2641 // TODO: Redo using MetaTableAccessor class 2642 Table t = getConnection().getTable(TableName.META_TABLE_NAME); 2643 List<byte[]> rows = new ArrayList<>(); 2644 ResultScanner s = t.getScanner(new Scan()); 2645 for (Result result : s) { 2646 LOG.info("getMetaTableRows: row -> " + Bytes.toStringBinary(result.getRow())); 2647 rows.add(result.getRow()); 2648 } 2649 s.close(); 2650 t.close(); 2651 return rows; 2652 } 2653 2654 /** 2655 * Returns all rows from the hbase:meta table for a given user table 2656 * @throws IOException When reading the rows fails. 2657 */ 2658 public List<byte[]> getMetaTableRows(TableName tableName) throws IOException { 2659 // TODO: Redo using MetaTableAccessor. 2660 Table t = getConnection().getTable(TableName.META_TABLE_NAME); 2661 List<byte[]> rows = new ArrayList<>(); 2662 ResultScanner s = t.getScanner(new Scan()); 2663 for (Result result : s) { 2664 RegionInfo info = MetaTableAccessor.getRegionInfo(result); 2665 if (info == null) { 2666 LOG.error("No region info for row " + Bytes.toString(result.getRow())); 2667 // TODO figure out what to do for this new hosed case. 2668 continue; 2669 } 2670 2671 if (info.getTable().equals(tableName)) { 2672 LOG.info("getMetaTableRows: row -> " + Bytes.toStringBinary(result.getRow()) + info); 2673 rows.add(result.getRow()); 2674 } 2675 } 2676 s.close(); 2677 t.close(); 2678 return rows; 2679 } 2680 2681 /** 2682 * Returns all regions of the specified table 2683 * @param tableName the table name 2684 * @return all regions of the specified table 2685 * @throws IOException when getting the regions fails. 2686 */ 2687 private List<RegionInfo> getRegions(TableName tableName) throws IOException { 2688 try (Admin admin = getConnection().getAdmin()) { 2689 return admin.getRegions(tableName); 2690 } 2691 } 2692 2693 /** 2694 * Find any other region server which is different from the one identified by parameter 2695 * @return another region server 2696 */ 2697 public HRegionServer getOtherRegionServer(HRegionServer rs) { 2698 for (JVMClusterUtil.RegionServerThread rst : getMiniHBaseCluster().getRegionServerThreads()) { 2699 if (!(rst.getRegionServer() == rs)) { 2700 return rst.getRegionServer(); 2701 } 2702 } 2703 return null; 2704 } 2705 2706 /** 2707 * Tool to get the reference to the region server object that holds the region of the specified 2708 * user table. 2709 * @param tableName user table to lookup in hbase:meta 2710 * @return region server that holds it, null if the row doesn't exist 2711 */ 2712 public HRegionServer getRSForFirstRegionInTable(TableName tableName) 2713 throws IOException, InterruptedException { 2714 List<RegionInfo> regions = getRegions(tableName); 2715 if (regions == null || regions.isEmpty()) { 2716 return null; 2717 } 2718 LOG.debug("Found " + regions.size() + " regions for table " + tableName); 2719 2720 byte[] firstRegionName = 2721 regions.stream().filter(r -> !r.isOffline()).map(RegionInfo::getRegionName).findFirst() 2722 .orElseThrow(() -> new IOException("online regions not found in table " + tableName)); 2723 2724 LOG.debug("firstRegionName=" + Bytes.toString(firstRegionName)); 2725 long pause = getConfiguration().getLong(HConstants.HBASE_CLIENT_PAUSE, 2726 HConstants.DEFAULT_HBASE_CLIENT_PAUSE); 2727 int numRetries = getConfiguration().getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 2728 HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER); 2729 RetryCounter retrier = new RetryCounter(numRetries + 1, (int) pause, TimeUnit.MICROSECONDS); 2730 while (retrier.shouldRetry()) { 2731 int index = getMiniHBaseCluster().getServerWith(firstRegionName); 2732 if (index != -1) { 2733 return getMiniHBaseCluster().getRegionServerThreads().get(index).getRegionServer(); 2734 } 2735 // Came back -1. Region may not be online yet. Sleep a while. 2736 retrier.sleepUntilNextRetry(); 2737 } 2738 return null; 2739 } 2740 2741 /** 2742 * Starts a <code>MiniMRCluster</code> with a default number of <code>TaskTracker</code>'s. 2743 * @throws IOException When starting the cluster fails. 2744 */ 2745 public MiniMRCluster startMiniMapReduceCluster() throws IOException { 2746 // Set a very high max-disk-utilization percentage to avoid the NodeManagers from failing. 2747 conf.setIfUnset("yarn.nodemanager.disk-health-checker.max-disk-utilization-per-disk-percentage", 2748 "99.0"); 2749 startMiniMapReduceCluster(2); 2750 return mrCluster; 2751 } 2752 2753 /** 2754 * Tasktracker has a bug where changing the hadoop.log.dir system property will not change its 2755 * internal static LOG_DIR variable. 2756 */ 2757 private void forceChangeTaskLogDir() { 2758 Field logDirField; 2759 try { 2760 logDirField = TaskLog.class.getDeclaredField("LOG_DIR"); 2761 logDirField.setAccessible(true); 2762 2763 Field modifiersField = ReflectionUtils.getModifiersField(); 2764 modifiersField.setAccessible(true); 2765 modifiersField.setInt(logDirField, logDirField.getModifiers() & ~Modifier.FINAL); 2766 2767 logDirField.set(null, new File(hadoopLogDir, "userlogs")); 2768 } catch (SecurityException e) { 2769 throw new RuntimeException(e); 2770 } catch (NoSuchFieldException e) { 2771 // TODO Auto-generated catch block 2772 throw new RuntimeException(e); 2773 } catch (IllegalArgumentException e) { 2774 throw new RuntimeException(e); 2775 } catch (IllegalAccessException e) { 2776 throw new RuntimeException(e); 2777 } 2778 } 2779 2780 /** 2781 * Starts a <code>MiniMRCluster</code>. Call {@link #setFileSystemURI(String)} to use a different 2782 * filesystem. 2783 * @param servers The number of <code>TaskTracker</code>'s to start. 2784 * @throws IOException When starting the cluster fails. 2785 */ 2786 private void startMiniMapReduceCluster(final int servers) throws IOException { 2787 if (mrCluster != null) { 2788 throw new IllegalStateException("MiniMRCluster is already running"); 2789 } 2790 LOG.info("Starting mini mapreduce cluster..."); 2791 setupClusterTestDir(); 2792 createDirsAndSetProperties(); 2793 2794 forceChangeTaskLogDir(); 2795 2796 //// hadoop2 specific settings 2797 // Tests were failing because this process used 6GB of virtual memory and was getting killed. 2798 // we up the VM usable so that processes don't get killed. 2799 conf.setFloat("yarn.nodemanager.vmem-pmem-ratio", 8.0f); 2800 2801 // Tests were failing due to MAPREDUCE-4880 / MAPREDUCE-4607 against hadoop 2.0.2-alpha and 2802 // this avoids the problem by disabling speculative task execution in tests. 2803 conf.setBoolean("mapreduce.map.speculative", false); 2804 conf.setBoolean("mapreduce.reduce.speculative", false); 2805 //// 2806 2807 // Yarn container runs in independent JVM. We need to pass the argument manually here if the 2808 // JDK version >= 17. Otherwise, the MiniMRCluster will fail. 2809 if (JVM.getJVMSpecVersion() >= 17) { 2810 String jvmOpts = conf.get("yarn.app.mapreduce.am.command-opts", ""); 2811 conf.set("yarn.app.mapreduce.am.command-opts", 2812 jvmOpts + " --add-opens java.base/java.lang=ALL-UNNAMED"); 2813 } 2814 2815 // Allow the user to override FS URI for this map-reduce cluster to use. 2816 mrCluster = 2817 new MiniMRCluster(servers, FS_URI != null ? FS_URI : FileSystem.get(conf).getUri().toString(), 2818 1, null, null, new JobConf(this.conf)); 2819 JobConf jobConf = MapreduceTestingShim.getJobConf(mrCluster); 2820 if (jobConf == null) { 2821 jobConf = mrCluster.createJobConf(); 2822 } 2823 // Hadoop MiniMR overwrites this while it should not 2824 jobConf.set("mapreduce.cluster.local.dir", conf.get("mapreduce.cluster.local.dir")); 2825 LOG.info("Mini mapreduce cluster started"); 2826 2827 // In hadoop2, YARN/MR2 starts a mini cluster with its own conf instance and updates settings. 2828 // Our HBase MR jobs need several of these settings in order to properly run. So we copy the 2829 // necessary config properties here. YARN-129 required adding a few properties. 2830 conf.set("mapreduce.jobtracker.address", jobConf.get("mapreduce.jobtracker.address")); 2831 // this for mrv2 support; mr1 ignores this 2832 conf.set("mapreduce.framework.name", "yarn"); 2833 conf.setBoolean("yarn.is.minicluster", true); 2834 String rmAddress = jobConf.get("yarn.resourcemanager.address"); 2835 if (rmAddress != null) { 2836 conf.set("yarn.resourcemanager.address", rmAddress); 2837 } 2838 String historyAddress = jobConf.get("mapreduce.jobhistory.address"); 2839 if (historyAddress != null) { 2840 conf.set("mapreduce.jobhistory.address", historyAddress); 2841 } 2842 String schedulerAddress = jobConf.get("yarn.resourcemanager.scheduler.address"); 2843 if (schedulerAddress != null) { 2844 conf.set("yarn.resourcemanager.scheduler.address", schedulerAddress); 2845 } 2846 String mrJobHistoryWebappAddress = jobConf.get("mapreduce.jobhistory.webapp.address"); 2847 if (mrJobHistoryWebappAddress != null) { 2848 conf.set("mapreduce.jobhistory.webapp.address", mrJobHistoryWebappAddress); 2849 } 2850 String yarnRMWebappAddress = jobConf.get("yarn.resourcemanager.webapp.address"); 2851 if (yarnRMWebappAddress != null) { 2852 conf.set("yarn.resourcemanager.webapp.address", yarnRMWebappAddress); 2853 } 2854 } 2855 2856 /** 2857 * Stops the previously started <code>MiniMRCluster</code>. 2858 */ 2859 public void shutdownMiniMapReduceCluster() { 2860 if (mrCluster != null) { 2861 LOG.info("Stopping mini mapreduce cluster..."); 2862 mrCluster.shutdown(); 2863 mrCluster = null; 2864 LOG.info("Mini mapreduce cluster stopped"); 2865 } 2866 // Restore configuration to point to local jobtracker 2867 conf.set("mapreduce.jobtracker.address", "local"); 2868 } 2869 2870 /** 2871 * Create a stubbed out RegionServerService, mainly for getting FS. 2872 */ 2873 public RegionServerServices createMockRegionServerService() throws IOException { 2874 return createMockRegionServerService((ServerName) null); 2875 } 2876 2877 /** 2878 * Create a stubbed out RegionServerService, mainly for getting FS. This version is used by 2879 * TestTokenAuthentication 2880 */ 2881 public RegionServerServices createMockRegionServerService(RpcServerInterface rpc) 2882 throws IOException { 2883 final MockRegionServerServices rss = new MockRegionServerServices(getZooKeeperWatcher()); 2884 rss.setFileSystem(getTestFileSystem()); 2885 rss.setRpcServer(rpc); 2886 return rss; 2887 } 2888 2889 /** 2890 * Create a stubbed out RegionServerService, mainly for getting FS. This version is used by 2891 * TestOpenRegionHandler 2892 */ 2893 public RegionServerServices createMockRegionServerService(ServerName name) throws IOException { 2894 final MockRegionServerServices rss = new MockRegionServerServices(getZooKeeperWatcher(), name); 2895 rss.setFileSystem(getTestFileSystem()); 2896 return rss; 2897 } 2898 2899 /** 2900 * Switches the logger for the given class to DEBUG level. 2901 * @param clazz The class for which to switch to debug logging. 2902 * @deprecated In 2.3.0, will be removed in 4.0.0. Only support changing log level on log4j now as 2903 * HBase only uses log4j. You should do this by your own as it you know which log 2904 * framework you are using then set the log level to debug is very easy. 2905 */ 2906 @Deprecated 2907 public void enableDebug(Class<?> clazz) { 2908 Log4jUtils.enableDebug(clazz); 2909 } 2910 2911 /** 2912 * Expire the Master's session 2913 */ 2914 public void expireMasterSession() throws Exception { 2915 HMaster master = getMiniHBaseCluster().getMaster(); 2916 expireSession(master.getZooKeeper(), false); 2917 } 2918 2919 /** 2920 * Expire a region server's session 2921 * @param index which RS 2922 */ 2923 public void expireRegionServerSession(int index) throws Exception { 2924 HRegionServer rs = getMiniHBaseCluster().getRegionServer(index); 2925 expireSession(rs.getZooKeeper(), false); 2926 decrementMinRegionServerCount(); 2927 } 2928 2929 private void decrementMinRegionServerCount() { 2930 // decrement the count for this.conf, for newly spwaned master 2931 // this.hbaseCluster shares this configuration too 2932 decrementMinRegionServerCount(getConfiguration()); 2933 2934 // each master thread keeps a copy of configuration 2935 for (MasterThread master : getHBaseCluster().getMasterThreads()) { 2936 decrementMinRegionServerCount(master.getMaster().getConfiguration()); 2937 } 2938 } 2939 2940 private void decrementMinRegionServerCount(Configuration conf) { 2941 int currentCount = conf.getInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, -1); 2942 if (currentCount != -1) { 2943 conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, Math.max(currentCount - 1, 1)); 2944 } 2945 } 2946 2947 public void expireSession(ZKWatcher nodeZK) throws Exception { 2948 expireSession(nodeZK, false); 2949 } 2950 2951 /** 2952 * Expire a ZooKeeper session as recommended in ZooKeeper documentation 2953 * http://hbase.apache.org/book.html#trouble.zookeeper There are issues when doing this: [1] 2954 * http://www.mail-archive.com/dev@zookeeper.apache.org/msg01942.html [2] 2955 * https://issues.apache.org/jira/browse/ZOOKEEPER-1105 2956 * @param nodeZK - the ZK watcher to expire 2957 * @param checkStatus - true to check if we can create a Table with the current configuration. 2958 */ 2959 public void expireSession(ZKWatcher nodeZK, boolean checkStatus) throws Exception { 2960 Configuration c = new Configuration(this.conf); 2961 String quorumServers = ZKConfig.getZKQuorumServersString(c); 2962 ZooKeeper zk = nodeZK.getRecoverableZooKeeper().getZooKeeper(); 2963 byte[] password = zk.getSessionPasswd(); 2964 long sessionID = zk.getSessionId(); 2965 2966 // Expiry seems to be asynchronous (see comment from P. Hunt in [1]), 2967 // so we create a first watcher to be sure that the 2968 // event was sent. We expect that if our watcher receives the event 2969 // other watchers on the same machine will get is as well. 2970 // When we ask to close the connection, ZK does not close it before 2971 // we receive all the events, so don't have to capture the event, just 2972 // closing the connection should be enough. 2973 ZooKeeper monitor = new ZooKeeper(quorumServers, 1000, new org.apache.zookeeper.Watcher() { 2974 @Override 2975 public void process(WatchedEvent watchedEvent) { 2976 LOG.info("Monitor ZKW received event=" + watchedEvent); 2977 } 2978 }, sessionID, password); 2979 2980 // Making it expire 2981 ZooKeeper newZK = 2982 new ZooKeeper(quorumServers, 1000, EmptyWatcher.instance, sessionID, password); 2983 2984 // ensure that we have connection to the server before closing down, otherwise 2985 // the close session event will be eaten out before we start CONNECTING state 2986 long start = EnvironmentEdgeManager.currentTime(); 2987 while ( 2988 newZK.getState() != States.CONNECTED && EnvironmentEdgeManager.currentTime() - start < 1000 2989 ) { 2990 Thread.sleep(1); 2991 } 2992 newZK.close(); 2993 LOG.info("ZK Closed Session 0x" + Long.toHexString(sessionID)); 2994 2995 // Now closing & waiting to be sure that the clients get it. 2996 monitor.close(); 2997 2998 if (checkStatus) { 2999 getConnection().getTable(TableName.META_TABLE_NAME).close(); 3000 } 3001 } 3002 3003 /** 3004 * Get the Mini HBase cluster. 3005 * @return hbase cluster 3006 * @see #getHBaseClusterInterface() 3007 */ 3008 public MiniHBaseCluster getHBaseCluster() { 3009 return getMiniHBaseCluster(); 3010 } 3011 3012 /** 3013 * Returns the HBaseCluster instance. 3014 * <p> 3015 * Returned object can be any of the subclasses of HBaseCluster, and the tests referring this 3016 * should not assume that the cluster is a mini cluster or a distributed one. If the test only 3017 * works on a mini cluster, then specific method {@link #getMiniHBaseCluster()} can be used 3018 * instead w/o the need to type-cast. 3019 */ 3020 public HBaseCluster getHBaseClusterInterface() { 3021 // implementation note: we should rename this method as #getHBaseCluster(), 3022 // but this would require refactoring 90+ calls. 3023 return hbaseCluster; 3024 } 3025 3026 /** 3027 * Resets the connections so that the next time getConnection() is called, a new connection is 3028 * created. This is needed in cases where the entire cluster / all the masters are shutdown and 3029 * the connection is not valid anymore. TODO: There should be a more coherent way of doing this. 3030 * Unfortunately the way tests are written, not all start() stop() calls go through this class. 3031 * Most tests directly operate on the underlying mini/local hbase cluster. That makes it difficult 3032 * for this wrapper class to maintain the connection state automatically. Cleaning this is a much 3033 * bigger refactor. 3034 */ 3035 public void invalidateConnection() throws IOException { 3036 closeConnection(); 3037 // Update the master addresses if they changed. 3038 final String masterConfigBefore = conf.get(HConstants.MASTER_ADDRS_KEY); 3039 final String masterConfAfter = getMiniHBaseCluster().conf.get(HConstants.MASTER_ADDRS_KEY); 3040 LOG.info("Invalidated connection. Updating master addresses before: {} after: {}", 3041 masterConfigBefore, masterConfAfter); 3042 conf.set(HConstants.MASTER_ADDRS_KEY, 3043 getMiniHBaseCluster().conf.get(HConstants.MASTER_ADDRS_KEY)); 3044 } 3045 3046 /** 3047 * Get a shared Connection to the cluster. this method is threadsafe. 3048 * @return A Connection that can be shared. Don't close. Will be closed on shutdown of cluster. 3049 */ 3050 public Connection getConnection() throws IOException { 3051 try { 3052 return this.connection.updateAndGet(connection -> { 3053 if (connection == null) { 3054 try { 3055 connection = ConnectionFactory.createConnection(this.conf); 3056 } catch (IOException ioe) { 3057 throw new UncheckedIOException("Failed to create connection", ioe); 3058 } 3059 } 3060 return connection; 3061 }); 3062 } catch (UncheckedIOException exception) { 3063 throw exception.getCause(); 3064 } 3065 } 3066 3067 /** 3068 * Returns a Admin instance. This instance is shared between HBaseTestingUtility instance users. 3069 * Closing it has no effect, it will be closed automatically when the cluster shutdowns 3070 * @return HBaseAdmin instance which is guaranteed to support only {@link Admin} interface. 3071 * Functions in HBaseAdmin not provided by {@link Admin} interface can be changed/deleted 3072 * anytime. 3073 * @deprecated Since 2.0. Will be removed in 3.0. Use {@link #getAdmin()} instead. 3074 */ 3075 @Deprecated 3076 public synchronized HBaseAdmin getHBaseAdmin() throws IOException { 3077 if (hbaseAdmin == null) { 3078 this.hbaseAdmin = (HBaseAdmin) getConnection().getAdmin(); 3079 } 3080 return hbaseAdmin; 3081 } 3082 3083 public void closeConnection() throws IOException { 3084 if (hbaseAdmin != null) { 3085 Closeables.close(hbaseAdmin, true); 3086 hbaseAdmin = null; 3087 } 3088 Connection connection = this.connection.getAndSet(null); 3089 if (connection != null) { 3090 Closeables.close(connection, true); 3091 } 3092 } 3093 3094 /** 3095 * Returns an Admin instance which is shared between HBaseTestingUtility instance users. Closing 3096 * it has no effect, it will be closed automatically when the cluster shutdowns 3097 */ 3098 public synchronized Admin getAdmin() throws IOException { 3099 if (hbaseAdmin == null) { 3100 this.hbaseAdmin = (HBaseAdmin) getConnection().getAdmin(); 3101 } 3102 return hbaseAdmin; 3103 } 3104 3105 private HBaseAdmin hbaseAdmin = null; 3106 3107 /** 3108 * Returns an {@link Hbck} instance. Needs be closed when done. 3109 */ 3110 public Hbck getHbck() throws IOException { 3111 return getConnection().getHbck(); 3112 } 3113 3114 /** 3115 * Unassign the named region. 3116 * @param regionName The region to unassign. 3117 */ 3118 public void unassignRegion(String regionName) throws IOException { 3119 unassignRegion(Bytes.toBytes(regionName)); 3120 } 3121 3122 /** 3123 * Unassign the named region. 3124 * @param regionName The region to unassign. 3125 */ 3126 public void unassignRegion(byte[] regionName) throws IOException { 3127 getAdmin().unassign(regionName, true); 3128 } 3129 3130 /** 3131 * Closes the region containing the given row. 3132 * @param row The row to find the containing region. 3133 * @param table The table to find the region. 3134 */ 3135 public void unassignRegionByRow(String row, RegionLocator table) throws IOException { 3136 unassignRegionByRow(Bytes.toBytes(row), table); 3137 } 3138 3139 /** 3140 * Closes the region containing the given row. 3141 * @param row The row to find the containing region. 3142 * @param table The table to find the region. 3143 */ 3144 public void unassignRegionByRow(byte[] row, RegionLocator table) throws IOException { 3145 HRegionLocation hrl = table.getRegionLocation(row); 3146 unassignRegion(hrl.getRegionInfo().getRegionName()); 3147 } 3148 3149 /** 3150 * Retrieves a splittable region randomly from tableName 3151 * @param tableName name of table 3152 * @param maxAttempts maximum number of attempts, unlimited for value of -1 3153 * @return the HRegion chosen, null if none was found within limit of maxAttempts 3154 */ 3155 public HRegion getSplittableRegion(TableName tableName, int maxAttempts) { 3156 List<HRegion> regions = getHBaseCluster().getRegions(tableName); 3157 int regCount = regions.size(); 3158 Set<Integer> attempted = new HashSet<>(); 3159 int idx; 3160 int attempts = 0; 3161 do { 3162 regions = getHBaseCluster().getRegions(tableName); 3163 if (regCount != regions.size()) { 3164 // if there was region movement, clear attempted Set 3165 attempted.clear(); 3166 } 3167 regCount = regions.size(); 3168 // There are chances that before we get the region for the table from an RS the region may 3169 // be going for CLOSE. This may be because online schema change is enabled 3170 if (regCount > 0) { 3171 idx = ThreadLocalRandom.current().nextInt(regCount); 3172 // if we have just tried this region, there is no need to try again 3173 if (attempted.contains(idx)) { 3174 continue; 3175 } 3176 HRegion region = regions.get(idx); 3177 if (region.checkSplit().isPresent()) { 3178 return region; 3179 } 3180 attempted.add(idx); 3181 } 3182 attempts++; 3183 } while (maxAttempts == -1 || attempts < maxAttempts); 3184 return null; 3185 } 3186 3187 public MiniDFSCluster getDFSCluster() { 3188 return dfsCluster; 3189 } 3190 3191 public void setDFSCluster(MiniDFSCluster cluster) throws IllegalStateException, IOException { 3192 setDFSCluster(cluster, true); 3193 } 3194 3195 /** 3196 * Set the MiniDFSCluster 3197 * @param cluster cluster to use 3198 * @param requireDown require the that cluster not be "up" (MiniDFSCluster#isClusterUp) before it 3199 * is set. 3200 * @throws IllegalStateException if the passed cluster is up when it is required to be down 3201 * @throws IOException if the FileSystem could not be set from the passed dfs cluster 3202 */ 3203 public void setDFSCluster(MiniDFSCluster cluster, boolean requireDown) 3204 throws IllegalStateException, IOException { 3205 if (dfsCluster != null && requireDown && dfsCluster.isClusterUp()) { 3206 throw new IllegalStateException("DFSCluster is already running! Shut it down first."); 3207 } 3208 this.dfsCluster = cluster; 3209 this.setFs(); 3210 } 3211 3212 public FileSystem getTestFileSystem() throws IOException { 3213 return HFileSystem.get(conf); 3214 } 3215 3216 /** 3217 * Wait until all regions in a table have been assigned. Waits default timeout before giving up 3218 * (30 seconds). 3219 * @param table Table to wait on. 3220 */ 3221 public void waitTableAvailable(TableName table) throws InterruptedException, IOException { 3222 waitTableAvailable(table.getName(), 30000); 3223 } 3224 3225 public void waitTableAvailable(TableName table, long timeoutMillis) 3226 throws InterruptedException, IOException { 3227 waitFor(timeoutMillis, predicateTableAvailable(table)); 3228 } 3229 3230 /** 3231 * Wait until all regions in a table have been assigned 3232 * @param table Table to wait on. 3233 * @param timeoutMillis Timeout. 3234 */ 3235 public void waitTableAvailable(byte[] table, long timeoutMillis) 3236 throws InterruptedException, IOException { 3237 waitFor(timeoutMillis, predicateTableAvailable(TableName.valueOf(table))); 3238 } 3239 3240 public String explainTableAvailability(TableName tableName) throws IOException { 3241 String msg = explainTableState(tableName, TableState.State.ENABLED) + ", "; 3242 if (getHBaseCluster().getMaster().isAlive()) { 3243 Map<RegionInfo, ServerName> assignments = getHBaseCluster().getMaster().getAssignmentManager() 3244 .getRegionStates().getRegionAssignments(); 3245 final List<Pair<RegionInfo, ServerName>> metaLocations = 3246 MetaTableAccessor.getTableRegionsAndLocations(getConnection(), tableName); 3247 for (Pair<RegionInfo, ServerName> metaLocation : metaLocations) { 3248 RegionInfo hri = metaLocation.getFirst(); 3249 ServerName sn = metaLocation.getSecond(); 3250 if (!assignments.containsKey(hri)) { 3251 msg += ", region " + hri + " not assigned, but found in meta, it expected to be on " + sn; 3252 3253 } else if (sn == null) { 3254 msg += ", region " + hri + " assigned, but has no server in meta"; 3255 } else if (!sn.equals(assignments.get(hri))) { 3256 msg += ", region " + hri + " assigned, but has different servers in meta and AM ( " + sn 3257 + " <> " + assignments.get(hri); 3258 } 3259 } 3260 } 3261 return msg; 3262 } 3263 3264 public String explainTableState(final TableName table, TableState.State state) 3265 throws IOException { 3266 TableState tableState = MetaTableAccessor.getTableState(getConnection(), table); 3267 if (tableState == null) { 3268 return "TableState in META: No table state in META for table " + table 3269 + " last state in meta (including deleted is " + findLastTableState(table) + ")"; 3270 } else if (!tableState.inStates(state)) { 3271 return "TableState in META: Not " + state + " state, but " + tableState; 3272 } else { 3273 return "TableState in META: OK"; 3274 } 3275 } 3276 3277 @Nullable 3278 public TableState findLastTableState(final TableName table) throws IOException { 3279 final AtomicReference<TableState> lastTableState = new AtomicReference<>(null); 3280 MetaTableAccessor.Visitor visitor = new MetaTableAccessor.Visitor() { 3281 @Override 3282 public boolean visit(Result r) throws IOException { 3283 if (!Arrays.equals(r.getRow(), table.getName())) return false; 3284 TableState state = MetaTableAccessor.getTableState(r); 3285 if (state != null) lastTableState.set(state); 3286 return true; 3287 } 3288 }; 3289 MetaTableAccessor.scanMeta(getConnection(), null, null, MetaTableAccessor.QueryType.TABLE, 3290 Integer.MAX_VALUE, visitor); 3291 return lastTableState.get(); 3292 } 3293 3294 /** 3295 * Waits for a table to be 'enabled'. Enabled means that table is set as 'enabled' and the regions 3296 * have been all assigned. Will timeout after default period (30 seconds) Tolerates nonexistent 3297 * table. 3298 * @param table the table to wait on. 3299 * @throws InterruptedException if interrupted while waiting 3300 * @throws IOException if an IO problem is encountered 3301 */ 3302 public void waitTableEnabled(TableName table) throws InterruptedException, IOException { 3303 waitTableEnabled(table, 30000); 3304 } 3305 3306 /** 3307 * Waits for a table to be 'enabled'. Enabled means that table is set as 'enabled' and the regions 3308 * have been all assigned. 3309 * @see #waitTableEnabled(TableName, long) 3310 * @param table Table to wait on. 3311 * @param timeoutMillis Time to wait on it being marked enabled. 3312 */ 3313 public void waitTableEnabled(byte[] table, long timeoutMillis) 3314 throws InterruptedException, IOException { 3315 waitTableEnabled(TableName.valueOf(table), timeoutMillis); 3316 } 3317 3318 public void waitTableEnabled(TableName table, long timeoutMillis) throws IOException { 3319 waitFor(timeoutMillis, predicateTableEnabled(table)); 3320 } 3321 3322 /** 3323 * Waits for a table to be 'disabled'. Disabled means that table is set as 'disabled' Will timeout 3324 * after default period (30 seconds) 3325 * @param table Table to wait on. 3326 */ 3327 public void waitTableDisabled(byte[] table) throws InterruptedException, IOException { 3328 waitTableDisabled(table, 30000); 3329 } 3330 3331 public void waitTableDisabled(TableName table, long millisTimeout) 3332 throws InterruptedException, IOException { 3333 waitFor(millisTimeout, predicateTableDisabled(table)); 3334 } 3335 3336 /** 3337 * Waits for a table to be 'disabled'. Disabled means that table is set as 'disabled' 3338 * @param table Table to wait on. 3339 * @param timeoutMillis Time to wait on it being marked disabled. 3340 */ 3341 public void waitTableDisabled(byte[] table, long timeoutMillis) 3342 throws InterruptedException, IOException { 3343 waitTableDisabled(TableName.valueOf(table), timeoutMillis); 3344 } 3345 3346 /** 3347 * Make sure that at least the specified number of region servers are running 3348 * @param num minimum number of region servers that should be running 3349 * @return true if we started some servers 3350 */ 3351 public boolean ensureSomeRegionServersAvailable(final int num) throws IOException { 3352 boolean startedServer = false; 3353 MiniHBaseCluster hbaseCluster = getMiniHBaseCluster(); 3354 for (int i = hbaseCluster.getLiveRegionServerThreads().size(); i < num; ++i) { 3355 LOG.info("Started new server=" + hbaseCluster.startRegionServer()); 3356 startedServer = true; 3357 } 3358 3359 return startedServer; 3360 } 3361 3362 /** 3363 * Make sure that at least the specified number of region servers are running. We don't count the 3364 * ones that are currently stopping or are stopped. 3365 * @param num minimum number of region servers that should be running 3366 * @return true if we started some servers 3367 */ 3368 public boolean ensureSomeNonStoppedRegionServersAvailable(final int num) throws IOException { 3369 boolean startedServer = ensureSomeRegionServersAvailable(num); 3370 3371 int nonStoppedServers = 0; 3372 for (JVMClusterUtil.RegionServerThread rst : getMiniHBaseCluster().getRegionServerThreads()) { 3373 3374 HRegionServer hrs = rst.getRegionServer(); 3375 if (hrs.isStopping() || hrs.isStopped()) { 3376 LOG.info("A region server is stopped or stopping:" + hrs); 3377 } else { 3378 nonStoppedServers++; 3379 } 3380 } 3381 for (int i = nonStoppedServers; i < num; ++i) { 3382 LOG.info("Started new server=" + getMiniHBaseCluster().startRegionServer()); 3383 startedServer = true; 3384 } 3385 return startedServer; 3386 } 3387 3388 /** 3389 * This method clones the passed <code>c</code> configuration setting a new user into the clone. 3390 * Use it getting new instances of FileSystem. Only works for DistributedFileSystem w/o Kerberos. 3391 * @param c Initial configuration 3392 * @param differentiatingSuffix Suffix to differentiate this user from others. 3393 * @return A new configuration instance with a different user set into it. 3394 */ 3395 public static User getDifferentUser(final Configuration c, final String differentiatingSuffix) 3396 throws IOException { 3397 FileSystem currentfs = FileSystem.get(c); 3398 if (!(currentfs instanceof DistributedFileSystem) || User.isHBaseSecurityEnabled(c)) { 3399 return User.getCurrent(); 3400 } 3401 // Else distributed filesystem. Make a new instance per daemon. Below 3402 // code is taken from the AppendTestUtil over in hdfs. 3403 String username = User.getCurrent().getName() + differentiatingSuffix; 3404 User user = User.createUserForTesting(c, username, new String[] { "supergroup" }); 3405 return user; 3406 } 3407 3408 public static NavigableSet<String> getAllOnlineRegions(MiniHBaseCluster cluster) 3409 throws IOException { 3410 NavigableSet<String> online = new TreeSet<>(); 3411 for (RegionServerThread rst : cluster.getLiveRegionServerThreads()) { 3412 try { 3413 for (RegionInfo region : ProtobufUtil 3414 .getOnlineRegions(rst.getRegionServer().getRSRpcServices())) { 3415 online.add(region.getRegionNameAsString()); 3416 } 3417 } catch (RegionServerStoppedException e) { 3418 // That's fine. 3419 } 3420 } 3421 for (MasterThread mt : cluster.getLiveMasterThreads()) { 3422 try { 3423 for (RegionInfo region : ProtobufUtil.getOnlineRegions(mt.getMaster().getRSRpcServices())) { 3424 online.add(region.getRegionNameAsString()); 3425 } 3426 } catch (RegionServerStoppedException e) { 3427 // That's fine. 3428 } catch (ServerNotRunningYetException e) { 3429 // That's fine. 3430 } 3431 } 3432 return online; 3433 } 3434 3435 /** 3436 * Set maxRecoveryErrorCount in DFSClient. In 0.20 pre-append its hard-coded to 5 and makes tests 3437 * linger. Here is the exception you'll see: 3438 * 3439 * <pre> 3440 * 2010-06-15 11:52:28,511 WARN [DataStreamer for file /hbase/.logs/wal.1276627923013 block 3441 * blk_928005470262850423_1021] hdfs.DFSClient$DFSOutputStream(2657): Error Recovery for block 3442 * blk_928005470262850423_1021 failed because recovery from primary datanode 127.0.0.1:53683 3443 * failed 4 times. Pipeline was 127.0.0.1:53687, 127.0.0.1:53683. Will retry... 3444 * </pre> 3445 * 3446 * @param stream A DFSClient.DFSOutputStream. 3447 */ 3448 public static void setMaxRecoveryErrorCount(final OutputStream stream, final int max) { 3449 try { 3450 Class<?>[] clazzes = DFSClient.class.getDeclaredClasses(); 3451 for (Class<?> clazz : clazzes) { 3452 String className = clazz.getSimpleName(); 3453 if (className.equals("DFSOutputStream")) { 3454 if (clazz.isInstance(stream)) { 3455 Field maxRecoveryErrorCountField = 3456 stream.getClass().getDeclaredField("maxRecoveryErrorCount"); 3457 maxRecoveryErrorCountField.setAccessible(true); 3458 maxRecoveryErrorCountField.setInt(stream, max); 3459 break; 3460 } 3461 } 3462 } 3463 } catch (Exception e) { 3464 LOG.info("Could not set max recovery field", e); 3465 } 3466 } 3467 3468 /** 3469 * Uses directly the assignment manager to assign the region. and waits until the specified region 3470 * has completed assignment. 3471 * @return true if the region is assigned false otherwise. 3472 */ 3473 public boolean assignRegion(final RegionInfo regionInfo) 3474 throws IOException, InterruptedException { 3475 final AssignmentManager am = getHBaseCluster().getMaster().getAssignmentManager(); 3476 am.assign(regionInfo); 3477 return AssignmentTestingUtil.waitForAssignment(am, regionInfo); 3478 } 3479 3480 /** 3481 * Move region to destination server and wait till region is completely moved and online 3482 * @param destRegion region to move 3483 * @param destServer destination server of the region 3484 */ 3485 public void moveRegionAndWait(RegionInfo destRegion, ServerName destServer) 3486 throws InterruptedException, IOException { 3487 HMaster master = getMiniHBaseCluster().getMaster(); 3488 // TODO: Here we start the move. The move can take a while. 3489 getAdmin().move(destRegion.getEncodedNameAsBytes(), destServer); 3490 while (true) { 3491 ServerName serverName = 3492 master.getAssignmentManager().getRegionStates().getRegionServerOfRegion(destRegion); 3493 if (serverName != null && serverName.equals(destServer)) { 3494 assertRegionOnServer(destRegion, serverName, 2000); 3495 break; 3496 } 3497 Thread.sleep(10); 3498 } 3499 } 3500 3501 /** 3502 * Wait until all regions for a table in hbase:meta have a non-empty info:server, up to a 3503 * configuable timeout value (default is 60 seconds) This means all regions have been deployed, 3504 * master has been informed and updated hbase:meta with the regions deployed server. 3505 * @param tableName the table name 3506 */ 3507 public void waitUntilAllRegionsAssigned(final TableName tableName) throws IOException { 3508 waitUntilAllRegionsAssigned(tableName, 3509 this.conf.getLong("hbase.client.sync.wait.timeout.msec", 60000)); 3510 } 3511 3512 /** 3513 * Waith until all system table's regions get assigned 3514 */ 3515 public void waitUntilAllSystemRegionsAssigned() throws IOException { 3516 waitUntilAllRegionsAssigned(TableName.META_TABLE_NAME); 3517 waitUntilAllRegionsAssigned(TableName.NAMESPACE_TABLE_NAME); 3518 } 3519 3520 /** 3521 * Wait until all regions for a table in hbase:meta have a non-empty info:server, or until 3522 * timeout. This means all regions have been deployed, master has been informed and updated 3523 * hbase:meta with the regions deployed server. 3524 * @param tableName the table name 3525 * @param timeout timeout, in milliseconds 3526 */ 3527 public void waitUntilAllRegionsAssigned(final TableName tableName, final long timeout) 3528 throws IOException { 3529 if (!TableName.isMetaTableName(tableName)) { 3530 try (final Table meta = getConnection().getTable(TableName.META_TABLE_NAME)) { 3531 LOG.debug("Waiting until all regions of table " + tableName + " get assigned. Timeout = " 3532 + timeout + "ms"); 3533 waitFor(timeout, 200, true, new ExplainingPredicate<IOException>() { 3534 @Override 3535 public String explainFailure() throws IOException { 3536 return explainTableAvailability(tableName); 3537 } 3538 3539 @Override 3540 public boolean evaluate() throws IOException { 3541 Scan scan = new Scan(); 3542 scan.addFamily(HConstants.CATALOG_FAMILY); 3543 boolean tableFound = false; 3544 try (ResultScanner s = meta.getScanner(scan)) { 3545 for (Result r; (r = s.next()) != null;) { 3546 byte[] b = r.getValue(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER); 3547 HRegionInfo info = HRegionInfo.parseFromOrNull(b); 3548 if (info != null && info.getTable().equals(tableName)) { 3549 // Get server hosting this region from catalog family. Return false if no server 3550 // hosting this region, or if the server hosting this region was recently killed 3551 // (for fault tolerance testing). 3552 tableFound = true; 3553 byte[] server = 3554 r.getValue(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER); 3555 if (server == null) { 3556 return false; 3557 } else { 3558 byte[] startCode = 3559 r.getValue(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER); 3560 ServerName serverName = 3561 ServerName.valueOf(Bytes.toString(server).replaceFirst(":", ",") + "," 3562 + Bytes.toLong(startCode)); 3563 if ( 3564 !getHBaseClusterInterface().isDistributedCluster() 3565 && getHBaseCluster().isKilledRS(serverName) 3566 ) { 3567 return false; 3568 } 3569 } 3570 if (RegionStateStore.getRegionState(r, info) != RegionState.State.OPEN) { 3571 return false; 3572 } 3573 } 3574 } 3575 } 3576 if (!tableFound) { 3577 LOG.warn( 3578 "Didn't find the entries for table " + tableName + " in meta, already deleted?"); 3579 } 3580 return tableFound; 3581 } 3582 }); 3583 } 3584 } 3585 LOG.info("All regions for table " + tableName + " assigned to meta. Checking AM states."); 3586 // check from the master state if we are using a mini cluster 3587 if (!getHBaseClusterInterface().isDistributedCluster()) { 3588 // So, all regions are in the meta table but make sure master knows of the assignments before 3589 // returning -- sometimes this can lag. 3590 HMaster master = getHBaseCluster().getMaster(); 3591 final RegionStates states = master.getAssignmentManager().getRegionStates(); 3592 waitFor(timeout, 200, new ExplainingPredicate<IOException>() { 3593 @Override 3594 public String explainFailure() throws IOException { 3595 return explainTableAvailability(tableName); 3596 } 3597 3598 @Override 3599 public boolean evaluate() throws IOException { 3600 List<RegionInfo> hris = states.getRegionsOfTable(tableName); 3601 return hris != null && !hris.isEmpty(); 3602 } 3603 }); 3604 } 3605 LOG.info("All regions for table " + tableName + " assigned."); 3606 } 3607 3608 /** 3609 * Do a small get/scan against one store. This is required because store has no actual methods of 3610 * querying itself, and relies on StoreScanner. 3611 */ 3612 public static List<Cell> getFromStoreFile(HStore store, Get get) throws IOException { 3613 Scan scan = new Scan(get); 3614 InternalScanner scanner = (InternalScanner) store.getScanner(scan, 3615 scan.getFamilyMap().get(store.getColumnFamilyDescriptor().getName()), 3616 // originally MultiVersionConcurrencyControl.resetThreadReadPoint() was called to set 3617 // readpoint 0. 3618 0); 3619 3620 List<Cell> result = new ArrayList<>(); 3621 scanner.next(result); 3622 if (!result.isEmpty()) { 3623 // verify that we are on the row we want: 3624 Cell kv = result.get(0); 3625 if (!CellUtil.matchingRows(kv, get.getRow())) { 3626 result.clear(); 3627 } 3628 } 3629 scanner.close(); 3630 return result; 3631 } 3632 3633 /** 3634 * Create region split keys between startkey and endKey 3635 * @param numRegions the number of regions to be created. it has to be greater than 3. 3636 * @return resulting split keys 3637 */ 3638 public byte[][] getRegionSplitStartKeys(byte[] startKey, byte[] endKey, int numRegions) { 3639 assertTrue(numRegions > 3); 3640 byte[][] tmpSplitKeys = Bytes.split(startKey, endKey, numRegions - 3); 3641 byte[][] result = new byte[tmpSplitKeys.length + 1][]; 3642 System.arraycopy(tmpSplitKeys, 0, result, 1, tmpSplitKeys.length); 3643 result[0] = HConstants.EMPTY_BYTE_ARRAY; 3644 return result; 3645 } 3646 3647 /** 3648 * Do a small get/scan against one store. This is required because store has no actual methods of 3649 * querying itself, and relies on StoreScanner. 3650 */ 3651 public static List<Cell> getFromStoreFile(HStore store, byte[] row, NavigableSet<byte[]> columns) 3652 throws IOException { 3653 Get get = new Get(row); 3654 Map<byte[], NavigableSet<byte[]>> s = get.getFamilyMap(); 3655 s.put(store.getColumnFamilyDescriptor().getName(), columns); 3656 3657 return getFromStoreFile(store, get); 3658 } 3659 3660 public static void assertKVListsEqual(String additionalMsg, final List<? extends Cell> expected, 3661 final List<? extends Cell> actual) { 3662 final int eLen = expected.size(); 3663 final int aLen = actual.size(); 3664 final int minLen = Math.min(eLen, aLen); 3665 3666 int i; 3667 for (i = 0; i < minLen 3668 && CellComparator.getInstance().compare(expected.get(i), actual.get(i)) == 0; ++i) { 3669 } 3670 3671 if (additionalMsg == null) { 3672 additionalMsg = ""; 3673 } 3674 if (!additionalMsg.isEmpty()) { 3675 additionalMsg = ". " + additionalMsg; 3676 } 3677 3678 if (eLen != aLen || i != minLen) { 3679 throw new AssertionError("Expected and actual KV arrays differ at position " + i + ": " 3680 + safeGetAsStr(expected, i) + " (length " + eLen + ") vs. " + safeGetAsStr(actual, i) 3681 + " (length " + aLen + ")" + additionalMsg); 3682 } 3683 } 3684 3685 public static <T> String safeGetAsStr(List<T> lst, int i) { 3686 if (0 <= i && i < lst.size()) { 3687 return lst.get(i).toString(); 3688 } else { 3689 return "<out_of_range>"; 3690 } 3691 } 3692 3693 public String getClusterKey() { 3694 return conf.get(HConstants.ZOOKEEPER_QUORUM) + ":" + conf.get(HConstants.ZOOKEEPER_CLIENT_PORT) 3695 + ":" 3696 + conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT, HConstants.DEFAULT_ZOOKEEPER_ZNODE_PARENT); 3697 } 3698 3699 /** Creates a random table with the given parameters */ 3700 public Table createRandomTable(TableName tableName, final Collection<String> families, 3701 final int maxVersions, final int numColsPerRow, final int numFlushes, final int numRegions, 3702 final int numRowsPerFlush) throws IOException, InterruptedException { 3703 3704 LOG.info("\n\nCreating random table " + tableName + " with " + numRegions + " regions, " 3705 + numFlushes + " storefiles per region, " + numRowsPerFlush + " rows per flush, maxVersions=" 3706 + maxVersions + "\n"); 3707 3708 final int numCF = families.size(); 3709 final byte[][] cfBytes = new byte[numCF][]; 3710 { 3711 int cfIndex = 0; 3712 for (String cf : families) { 3713 cfBytes[cfIndex++] = Bytes.toBytes(cf); 3714 } 3715 } 3716 3717 final int actualStartKey = 0; 3718 final int actualEndKey = Integer.MAX_VALUE; 3719 final int keysPerRegion = (actualEndKey - actualStartKey) / numRegions; 3720 final int splitStartKey = actualStartKey + keysPerRegion; 3721 final int splitEndKey = actualEndKey - keysPerRegion; 3722 final String keyFormat = "%08x"; 3723 final Table table = createTable(tableName, cfBytes, maxVersions, 3724 Bytes.toBytes(String.format(keyFormat, splitStartKey)), 3725 Bytes.toBytes(String.format(keyFormat, splitEndKey)), numRegions); 3726 3727 if (hbaseCluster != null) { 3728 getMiniHBaseCluster().flushcache(TableName.META_TABLE_NAME); 3729 } 3730 3731 BufferedMutator mutator = getConnection().getBufferedMutator(tableName); 3732 3733 final Random rand = ThreadLocalRandom.current(); 3734 for (int iFlush = 0; iFlush < numFlushes; ++iFlush) { 3735 for (int iRow = 0; iRow < numRowsPerFlush; ++iRow) { 3736 final byte[] row = Bytes.toBytes( 3737 String.format(keyFormat, actualStartKey + rand.nextInt(actualEndKey - actualStartKey))); 3738 3739 Put put = new Put(row); 3740 Delete del = new Delete(row); 3741 for (int iCol = 0; iCol < numColsPerRow; ++iCol) { 3742 final byte[] cf = cfBytes[rand.nextInt(numCF)]; 3743 final long ts = rand.nextInt(); 3744 final byte[] qual = Bytes.toBytes("col" + iCol); 3745 if (rand.nextBoolean()) { 3746 final byte[] value = 3747 Bytes.toBytes("value_for_row_" + iRow + "_cf_" + Bytes.toStringBinary(cf) + "_col_" 3748 + iCol + "_ts_" + ts + "_random_" + rand.nextLong()); 3749 put.addColumn(cf, qual, ts, value); 3750 } else if (rand.nextDouble() < 0.8) { 3751 del.addColumn(cf, qual, ts); 3752 } else { 3753 del.addColumns(cf, qual, ts); 3754 } 3755 } 3756 3757 if (!put.isEmpty()) { 3758 mutator.mutate(put); 3759 } 3760 3761 if (!del.isEmpty()) { 3762 mutator.mutate(del); 3763 } 3764 } 3765 LOG.info("Initiating flush #" + iFlush + " for table " + tableName); 3766 mutator.flush(); 3767 if (hbaseCluster != null) { 3768 getMiniHBaseCluster().flushcache(table.getName()); 3769 } 3770 } 3771 mutator.close(); 3772 3773 return table; 3774 } 3775 3776 public static int randomFreePort() { 3777 return HBaseCommonTestingUtility.randomFreePort(); 3778 } 3779 3780 public static String randomMultiCastAddress() { 3781 return "226.1.1." + ThreadLocalRandom.current().nextInt(254); 3782 } 3783 3784 public static void waitForHostPort(String host, int port) throws IOException { 3785 final int maxTimeMs = 10000; 3786 final int maxNumAttempts = maxTimeMs / HConstants.SOCKET_RETRY_WAIT_MS; 3787 IOException savedException = null; 3788 LOG.info("Waiting for server at " + host + ":" + port); 3789 for (int attempt = 0; attempt < maxNumAttempts; ++attempt) { 3790 try { 3791 Socket sock = new Socket(InetAddress.getByName(host), port); 3792 sock.close(); 3793 savedException = null; 3794 LOG.info("Server at " + host + ":" + port + " is available"); 3795 break; 3796 } catch (UnknownHostException e) { 3797 throw new IOException("Failed to look up " + host, e); 3798 } catch (IOException e) { 3799 savedException = e; 3800 } 3801 Threads.sleepWithoutInterrupt(HConstants.SOCKET_RETRY_WAIT_MS); 3802 } 3803 3804 if (savedException != null) { 3805 throw savedException; 3806 } 3807 } 3808 3809 /** 3810 * Creates a pre-split table for load testing. If the table already exists, logs a warning and 3811 * continues. 3812 * @return the number of regions the table was split into 3813 */ 3814 public static int createPreSplitLoadTestTable(Configuration conf, TableName tableName, 3815 byte[] columnFamily, Algorithm compression, DataBlockEncoding dataBlockEncoding) 3816 throws IOException { 3817 return createPreSplitLoadTestTable(conf, tableName, columnFamily, compression, 3818 dataBlockEncoding, DEFAULT_REGIONS_PER_SERVER, 1, Durability.USE_DEFAULT); 3819 } 3820 3821 /** 3822 * Creates a pre-split table for load testing. If the table already exists, logs a warning and 3823 * continues. 3824 * @return the number of regions the table was split into 3825 */ 3826 public static int createPreSplitLoadTestTable(Configuration conf, TableName tableName, 3827 byte[] columnFamily, Algorithm compression, DataBlockEncoding dataBlockEncoding, 3828 int numRegionsPerServer, int regionReplication, Durability durability) throws IOException { 3829 HTableDescriptor desc = new HTableDescriptor(tableName); 3830 desc.setDurability(durability); 3831 desc.setRegionReplication(regionReplication); 3832 HColumnDescriptor hcd = new HColumnDescriptor(columnFamily); 3833 hcd.setDataBlockEncoding(dataBlockEncoding); 3834 hcd.setCompressionType(compression); 3835 return createPreSplitLoadTestTable(conf, desc, hcd, numRegionsPerServer); 3836 } 3837 3838 /** 3839 * Creates a pre-split table for load testing. If the table already exists, logs a warning and 3840 * continues. 3841 * @return the number of regions the table was split into 3842 */ 3843 public static int createPreSplitLoadTestTable(Configuration conf, TableName tableName, 3844 byte[][] columnFamilies, Algorithm compression, DataBlockEncoding dataBlockEncoding, 3845 int numRegionsPerServer, int regionReplication, Durability durability) throws IOException { 3846 HTableDescriptor desc = new HTableDescriptor(tableName); 3847 desc.setDurability(durability); 3848 desc.setRegionReplication(regionReplication); 3849 HColumnDescriptor[] hcds = new HColumnDescriptor[columnFamilies.length]; 3850 for (int i = 0; i < columnFamilies.length; i++) { 3851 HColumnDescriptor hcd = new HColumnDescriptor(columnFamilies[i]); 3852 hcd.setDataBlockEncoding(dataBlockEncoding); 3853 hcd.setCompressionType(compression); 3854 hcds[i] = hcd; 3855 } 3856 return createPreSplitLoadTestTable(conf, desc, hcds, numRegionsPerServer); 3857 } 3858 3859 /** 3860 * Creates a pre-split table for load testing. If the table already exists, logs a warning and 3861 * continues. 3862 * @return the number of regions the table was split into 3863 */ 3864 public static int createPreSplitLoadTestTable(Configuration conf, TableDescriptor desc, 3865 ColumnFamilyDescriptor hcd) throws IOException { 3866 return createPreSplitLoadTestTable(conf, desc, hcd, DEFAULT_REGIONS_PER_SERVER); 3867 } 3868 3869 /** 3870 * Creates a pre-split table for load testing. If the table already exists, logs a warning and 3871 * continues. 3872 * @return the number of regions the table was split into 3873 */ 3874 public static int createPreSplitLoadTestTable(Configuration conf, TableDescriptor desc, 3875 ColumnFamilyDescriptor hcd, int numRegionsPerServer) throws IOException { 3876 return createPreSplitLoadTestTable(conf, desc, new ColumnFamilyDescriptor[] { hcd }, 3877 numRegionsPerServer); 3878 } 3879 3880 /** 3881 * Creates a pre-split table for load testing. If the table already exists, logs a warning and 3882 * continues. 3883 * @return the number of regions the table was split into 3884 */ 3885 public static int createPreSplitLoadTestTable(Configuration conf, TableDescriptor desc, 3886 ColumnFamilyDescriptor[] hcds, int numRegionsPerServer) throws IOException { 3887 return createPreSplitLoadTestTable(conf, desc, hcds, new RegionSplitter.HexStringSplit(), 3888 numRegionsPerServer); 3889 } 3890 3891 /** 3892 * Creates a pre-split table for load testing. If the table already exists, logs a warning and 3893 * continues. 3894 * @return the number of regions the table was split into 3895 */ 3896 public static int createPreSplitLoadTestTable(Configuration conf, TableDescriptor td, 3897 ColumnFamilyDescriptor[] cds, SplitAlgorithm splitter, int numRegionsPerServer) 3898 throws IOException { 3899 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(td); 3900 for (ColumnFamilyDescriptor cd : cds) { 3901 if (!td.hasColumnFamily(cd.getName())) { 3902 builder.setColumnFamily(cd); 3903 } 3904 } 3905 td = builder.build(); 3906 int totalNumberOfRegions = 0; 3907 Connection unmanagedConnection = ConnectionFactory.createConnection(conf); 3908 Admin admin = unmanagedConnection.getAdmin(); 3909 3910 try { 3911 // create a table a pre-splits regions. 3912 // The number of splits is set as: 3913 // region servers * regions per region server). 3914 int numberOfServers = admin.getRegionServers().size(); 3915 if (numberOfServers == 0) { 3916 throw new IllegalStateException("No live regionservers"); 3917 } 3918 3919 totalNumberOfRegions = numberOfServers * numRegionsPerServer; 3920 LOG.info("Number of live regionservers: " + numberOfServers + ", " 3921 + "pre-splitting table into " + totalNumberOfRegions + " regions " + "(regions per server: " 3922 + numRegionsPerServer + ")"); 3923 3924 byte[][] splits = splitter.split(totalNumberOfRegions); 3925 3926 admin.createTable(td, splits); 3927 } catch (MasterNotRunningException e) { 3928 LOG.error("Master not running", e); 3929 throw new IOException(e); 3930 } catch (TableExistsException e) { 3931 LOG.warn("Table " + td.getTableName() + " already exists, continuing"); 3932 } finally { 3933 admin.close(); 3934 unmanagedConnection.close(); 3935 } 3936 return totalNumberOfRegions; 3937 } 3938 3939 public static int getMetaRSPort(Connection connection) throws IOException { 3940 try (RegionLocator locator = connection.getRegionLocator(TableName.META_TABLE_NAME)) { 3941 return locator.getRegionLocation(Bytes.toBytes("")).getPort(); 3942 } 3943 } 3944 3945 /** 3946 * Due to async racing issue, a region may not be in the online region list of a region server 3947 * yet, after the assignment znode is deleted and the new assignment is recorded in master. 3948 */ 3949 public void assertRegionOnServer(final RegionInfo hri, final ServerName server, 3950 final long timeout) throws IOException, InterruptedException { 3951 long timeoutTime = EnvironmentEdgeManager.currentTime() + timeout; 3952 while (true) { 3953 List<RegionInfo> regions = getAdmin().getRegions(server); 3954 if (regions.stream().anyMatch(r -> RegionInfo.COMPARATOR.compare(r, hri) == 0)) return; 3955 long now = EnvironmentEdgeManager.currentTime(); 3956 if (now > timeoutTime) break; 3957 Thread.sleep(10); 3958 } 3959 fail("Could not find region " + hri.getRegionNameAsString() + " on server " + server); 3960 } 3961 3962 /** 3963 * Check to make sure the region is open on the specified region server, but not on any other one. 3964 */ 3965 public void assertRegionOnlyOnServer(final RegionInfo hri, final ServerName server, 3966 final long timeout) throws IOException, InterruptedException { 3967 long timeoutTime = EnvironmentEdgeManager.currentTime() + timeout; 3968 while (true) { 3969 List<RegionInfo> regions = getAdmin().getRegions(server); 3970 if (regions.stream().anyMatch(r -> RegionInfo.COMPARATOR.compare(r, hri) == 0)) { 3971 List<JVMClusterUtil.RegionServerThread> rsThreads = 3972 getHBaseCluster().getLiveRegionServerThreads(); 3973 for (JVMClusterUtil.RegionServerThread rsThread : rsThreads) { 3974 HRegionServer rs = rsThread.getRegionServer(); 3975 if (server.equals(rs.getServerName())) { 3976 continue; 3977 } 3978 Collection<HRegion> hrs = rs.getOnlineRegionsLocalContext(); 3979 for (HRegion r : hrs) { 3980 assertTrue("Region should not be double assigned", 3981 r.getRegionInfo().getRegionId() != hri.getRegionId()); 3982 } 3983 } 3984 return; // good, we are happy 3985 } 3986 long now = EnvironmentEdgeManager.currentTime(); 3987 if (now > timeoutTime) break; 3988 Thread.sleep(10); 3989 } 3990 fail("Could not find region " + hri.getRegionNameAsString() + " on server " + server); 3991 } 3992 3993 public HRegion createTestRegion(String tableName, ColumnFamilyDescriptor cd) throws IOException { 3994 TableDescriptor td = 3995 TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName)).setColumnFamily(cd).build(); 3996 RegionInfo info = RegionInfoBuilder.newBuilder(TableName.valueOf(tableName)).build(); 3997 return createRegionAndWAL(info, getDataTestDir(), getConfiguration(), td); 3998 } 3999 4000 public HRegion createTestRegion(String tableName, ColumnFamilyDescriptor cd, 4001 BlockCache blockCache) throws IOException { 4002 TableDescriptor td = 4003 TableDescriptorBuilder.newBuilder(TableName.valueOf(tableName)).setColumnFamily(cd).build(); 4004 RegionInfo info = RegionInfoBuilder.newBuilder(TableName.valueOf(tableName)).build(); 4005 return createRegionAndWAL(info, getDataTestDir(), getConfiguration(), td, blockCache); 4006 } 4007 4008 public void setFileSystemURI(String fsURI) { 4009 FS_URI = fsURI; 4010 } 4011 4012 /** 4013 * Returns a {@link Predicate} for checking that there are no regions in transition in master 4014 */ 4015 public ExplainingPredicate<IOException> predicateNoRegionsInTransition() { 4016 return new ExplainingPredicate<IOException>() { 4017 @Override 4018 public String explainFailure() throws IOException { 4019 final RegionStates regionStates = 4020 getMiniHBaseCluster().getMaster().getAssignmentManager().getRegionStates(); 4021 return "found in transition: " + regionStates.getRegionsInTransition().toString(); 4022 } 4023 4024 @Override 4025 public boolean evaluate() throws IOException { 4026 HMaster master = getMiniHBaseCluster().getMaster(); 4027 if (master == null) return false; 4028 AssignmentManager am = master.getAssignmentManager(); 4029 if (am == null) return false; 4030 return !am.hasRegionsInTransition(); 4031 } 4032 }; 4033 } 4034 4035 /** 4036 * Returns a {@link Predicate} for checking that table is enabled 4037 */ 4038 public Waiter.Predicate<IOException> predicateTableEnabled(final TableName tableName) { 4039 return new ExplainingPredicate<IOException>() { 4040 @Override 4041 public String explainFailure() throws IOException { 4042 return explainTableState(tableName, TableState.State.ENABLED); 4043 } 4044 4045 @Override 4046 public boolean evaluate() throws IOException { 4047 return getAdmin().tableExists(tableName) && getAdmin().isTableEnabled(tableName); 4048 } 4049 }; 4050 } 4051 4052 /** 4053 * Returns a {@link Predicate} for checking that table is enabled 4054 */ 4055 public Waiter.Predicate<IOException> predicateTableDisabled(final TableName tableName) { 4056 return new ExplainingPredicate<IOException>() { 4057 @Override 4058 public String explainFailure() throws IOException { 4059 return explainTableState(tableName, TableState.State.DISABLED); 4060 } 4061 4062 @Override 4063 public boolean evaluate() throws IOException { 4064 return getAdmin().isTableDisabled(tableName); 4065 } 4066 }; 4067 } 4068 4069 /** 4070 * Returns a {@link Predicate} for checking that table is enabled 4071 */ 4072 public Waiter.Predicate<IOException> predicateTableAvailable(final TableName tableName) { 4073 return new ExplainingPredicate<IOException>() { 4074 @Override 4075 public String explainFailure() throws IOException { 4076 return explainTableAvailability(tableName); 4077 } 4078 4079 @Override 4080 public boolean evaluate() throws IOException { 4081 boolean tableAvailable = getAdmin().isTableAvailable(tableName); 4082 if (tableAvailable) { 4083 try (Table table = getConnection().getTable(tableName)) { 4084 TableDescriptor htd = table.getDescriptor(); 4085 for (HRegionLocation loc : getConnection().getRegionLocator(tableName) 4086 .getAllRegionLocations()) { 4087 Scan scan = new Scan().withStartRow(loc.getRegionInfo().getStartKey()) 4088 .withStopRow(loc.getRegionInfo().getEndKey()).setOneRowLimit() 4089 .setMaxResultsPerColumnFamily(1).setCacheBlocks(false); 4090 for (byte[] family : htd.getColumnFamilyNames()) { 4091 scan.addFamily(family); 4092 } 4093 try (ResultScanner scanner = table.getScanner(scan)) { 4094 scanner.next(); 4095 } 4096 } 4097 } 4098 } 4099 return tableAvailable; 4100 } 4101 }; 4102 } 4103 4104 /** 4105 * Wait until no regions in transition. 4106 * @param timeout How long to wait. 4107 */ 4108 public void waitUntilNoRegionsInTransition(final long timeout) throws IOException { 4109 waitFor(timeout, predicateNoRegionsInTransition()); 4110 } 4111 4112 /** 4113 * Wait until no regions in transition. (time limit 15min) 4114 */ 4115 public void waitUntilNoRegionsInTransition() throws IOException { 4116 waitUntilNoRegionsInTransition(15 * 60000); 4117 } 4118 4119 /** 4120 * Wait until labels is ready in VisibilityLabelsCache. 4121 */ 4122 public void waitLabelAvailable(long timeoutMillis, final String... labels) { 4123 final VisibilityLabelsCache labelsCache = VisibilityLabelsCache.get(); 4124 waitFor(timeoutMillis, new Waiter.ExplainingPredicate<RuntimeException>() { 4125 4126 @Override 4127 public boolean evaluate() { 4128 for (String label : labels) { 4129 if (labelsCache.getLabelOrdinal(label) == 0) { 4130 return false; 4131 } 4132 } 4133 return true; 4134 } 4135 4136 @Override 4137 public String explainFailure() { 4138 for (String label : labels) { 4139 if (labelsCache.getLabelOrdinal(label) == 0) { 4140 return label + " is not available yet"; 4141 } 4142 } 4143 return ""; 4144 } 4145 }); 4146 } 4147 4148 /** 4149 * Create a set of column descriptors with the combination of compression, encoding, bloom codecs 4150 * available. 4151 * @return the list of column descriptors 4152 */ 4153 public static List<HColumnDescriptor> generateColumnDescriptors() { 4154 return generateColumnDescriptors(""); 4155 } 4156 4157 /** 4158 * Create a set of column descriptors with the combination of compression, encoding, bloom codecs 4159 * available. 4160 * @param prefix family names prefix 4161 * @return the list of column descriptors 4162 */ 4163 public static List<HColumnDescriptor> generateColumnDescriptors(final String prefix) { 4164 List<HColumnDescriptor> htds = new ArrayList<>(); 4165 long familyId = 0; 4166 for (Compression.Algorithm compressionType : getSupportedCompressionAlgorithms()) { 4167 for (DataBlockEncoding encodingType : DataBlockEncoding.values()) { 4168 for (BloomType bloomType : BloomType.values()) { 4169 String name = String.format("%s-cf-!@#&-%d!@#", prefix, familyId); 4170 HColumnDescriptor htd = new HColumnDescriptor(name); 4171 htd.setCompressionType(compressionType); 4172 htd.setDataBlockEncoding(encodingType); 4173 htd.setBloomFilterType(bloomType); 4174 htds.add(htd); 4175 familyId++; 4176 } 4177 } 4178 } 4179 return htds; 4180 } 4181 4182 /** 4183 * Get supported compression algorithms. 4184 * @return supported compression algorithms. 4185 */ 4186 public static Compression.Algorithm[] getSupportedCompressionAlgorithms() { 4187 String[] allAlgos = HFile.getSupportedCompressionAlgorithms(); 4188 List<Compression.Algorithm> supportedAlgos = new ArrayList<>(); 4189 for (String algoName : allAlgos) { 4190 try { 4191 Compression.Algorithm algo = Compression.getCompressionAlgorithmByName(algoName); 4192 algo.getCompressor(); 4193 supportedAlgos.add(algo); 4194 } catch (Throwable t) { 4195 // this algo is not available 4196 } 4197 } 4198 return supportedAlgos.toArray(new Algorithm[supportedAlgos.size()]); 4199 } 4200 4201 public Result getClosestRowBefore(Region r, byte[] row, byte[] family) throws IOException { 4202 Scan scan = new Scan(row); 4203 scan.setSmall(true); 4204 scan.setCaching(1); 4205 scan.setReversed(true); 4206 scan.addFamily(family); 4207 try (RegionScanner scanner = r.getScanner(scan)) { 4208 List<Cell> cells = new ArrayList<>(1); 4209 scanner.next(cells); 4210 if (r.getRegionInfo().isMetaRegion() && !isTargetTable(row, cells.get(0))) { 4211 return null; 4212 } 4213 return Result.create(cells); 4214 } 4215 } 4216 4217 private boolean isTargetTable(final byte[] inRow, Cell c) { 4218 String inputRowString = Bytes.toString(inRow); 4219 int i = inputRowString.indexOf(HConstants.DELIMITER); 4220 String outputRowString = Bytes.toString(c.getRowArray(), c.getRowOffset(), c.getRowLength()); 4221 int o = outputRowString.indexOf(HConstants.DELIMITER); 4222 return inputRowString.substring(0, i).equals(outputRowString.substring(0, o)); 4223 } 4224 4225 /** 4226 * Sets up {@link MiniKdc} for testing security. Uses {@link HBaseKerberosUtils} to set the given 4227 * keytab file as {@link HBaseKerberosUtils#KRB_KEYTAB_FILE}. FYI, there is also the easier-to-use 4228 * kerby KDC server and utility for using it, 4229 * {@link org.apache.hadoop.hbase.util.SimpleKdcServerUtil}. The kerby KDC server is preferred; 4230 * less baggage. It came in in HBASE-5291. 4231 */ 4232 public MiniKdc setupMiniKdc(File keytabFile) throws Exception { 4233 Properties conf = MiniKdc.createConf(); 4234 conf.put(MiniKdc.DEBUG, true); 4235 MiniKdc kdc = null; 4236 File dir = null; 4237 // There is time lag between selecting a port and trying to bind with it. It's possible that 4238 // another service captures the port in between which'll result in BindException. 4239 boolean bindException; 4240 int numTries = 0; 4241 do { 4242 try { 4243 bindException = false; 4244 dir = new File(getDataTestDir("kdc").toUri().getPath()); 4245 kdc = new MiniKdc(conf, dir); 4246 kdc.start(); 4247 } catch (BindException e) { 4248 FileUtils.deleteDirectory(dir); // clean directory 4249 numTries++; 4250 if (numTries == 3) { 4251 LOG.error("Failed setting up MiniKDC. Tried " + numTries + " times."); 4252 throw e; 4253 } 4254 LOG.error("BindException encountered when setting up MiniKdc. Trying again."); 4255 bindException = true; 4256 } 4257 } while (bindException); 4258 HBaseKerberosUtils.setKeytabFileForTesting(keytabFile.getAbsolutePath()); 4259 return kdc; 4260 } 4261 4262 public int getNumHFiles(final TableName tableName, final byte[] family) { 4263 int numHFiles = 0; 4264 for (RegionServerThread regionServerThread : getMiniHBaseCluster().getRegionServerThreads()) { 4265 numHFiles += getNumHFilesForRS(regionServerThread.getRegionServer(), tableName, family); 4266 } 4267 return numHFiles; 4268 } 4269 4270 public int getNumHFilesForRS(final HRegionServer rs, final TableName tableName, 4271 final byte[] family) { 4272 int numHFiles = 0; 4273 for (Region region : rs.getRegions(tableName)) { 4274 numHFiles += region.getStore(family).getStorefilesCount(); 4275 } 4276 return numHFiles; 4277 } 4278 4279 public void verifyTableDescriptorIgnoreTableName(TableDescriptor ltd, TableDescriptor rtd) { 4280 assertEquals(ltd.getValues().hashCode(), rtd.getValues().hashCode()); 4281 Collection<ColumnFamilyDescriptor> ltdFamilies = Arrays.asList(ltd.getColumnFamilies()); 4282 Collection<ColumnFamilyDescriptor> rtdFamilies = Arrays.asList(rtd.getColumnFamilies()); 4283 assertEquals(ltdFamilies.size(), rtdFamilies.size()); 4284 for (Iterator<ColumnFamilyDescriptor> it = ltdFamilies.iterator(), 4285 it2 = rtdFamilies.iterator(); it.hasNext();) { 4286 assertEquals(0, ColumnFamilyDescriptor.COMPARATOR.compare(it.next(), it2.next())); 4287 } 4288 } 4289 4290 /** 4291 * Await the successful return of {@code condition}, sleeping {@code sleepMillis} between 4292 * invocations. 4293 */ 4294 public static void await(final long sleepMillis, final BooleanSupplier condition) 4295 throws InterruptedException { 4296 try { 4297 while (!condition.getAsBoolean()) { 4298 Thread.sleep(sleepMillis); 4299 } 4300 } catch (RuntimeException e) { 4301 if (e.getCause() instanceof AssertionError) { 4302 throw (AssertionError) e.getCause(); 4303 } 4304 throw e; 4305 } 4306 } 4307}