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