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&lt;Map.Entry&lt;String, String&gt;&gt; i = conf.iterator(); i.hasNext();) {
706   *   Map.Entry&lt;String, String&gt; 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}