You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by zg...@apache.org on 2017/01/25 03:33:28 UTC
hbase git commit: HBASE-17500 Implement
getTable/creatTable/deleteTable/truncateTable methods
Repository: hbase
Updated Branches:
refs/heads/master 85d701892 -> 8aea84d72
HBASE-17500 Implement getTable/creatTable/deleteTable/truncateTable methods
Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/8aea84d7
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/8aea84d7
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/8aea84d7
Branch: refs/heads/master
Commit: 8aea84d7231fb7f6bb6edde149ec30c18186355c
Parents: 85d7018
Author: Guanghao Zhang <zg...@apache.org>
Authored: Wed Jan 25 11:30:11 2017 +0800
Committer: Guanghao Zhang <zg...@apache.org>
Committed: Wed Jan 25 11:30:11 2017 +0800
----------------------------------------------------------------------
.../hadoop/hbase/AsyncMetaTableAccessor.java | 2 +-
.../apache/hadoop/hbase/client/AsyncAdmin.java | 89 +++-
.../hadoop/hbase/client/AsyncHBaseAdmin.java | 318 +++++++++++++-
.../hadoop/hbase/client/TestAsyncAdmin.java | 427 ++++++++++++++++++-
4 files changed, 802 insertions(+), 34 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hbase/blob/8aea84d7/hbase-client/src/main/java/org/apache/hadoop/hbase/AsyncMetaTableAccessor.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/AsyncMetaTableAccessor.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/AsyncMetaTableAccessor.java
index 9187473..6b0d588 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/AsyncMetaTableAccessor.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/AsyncMetaTableAccessor.java
@@ -55,7 +55,7 @@ public class AsyncMetaTableAccessor {
return getTableState(conn, tableName).thenApply(Optional::isPresent);
}
- private static CompletableFuture<Optional<TableState>> getTableState(AsyncConnection conn,
+ public static CompletableFuture<Optional<TableState>> getTableState(AsyncConnection conn,
TableName tableName) {
CompletableFuture<Optional<TableState>> future = new CompletableFuture<>();
getMetaTable(conn).thenAccept((metaTable) -> {
http://git-wip-us.apache.org/repos/asf/hbase/blob/8aea84d7/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
index 20a6070..56036bf 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
@@ -19,10 +19,8 @@ package org.apache.hadoop.hbase.client;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
-
-import org.apache.hadoop.hbase.HTableDescriptor;
-import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.classification.InterfaceStability;
@@ -33,6 +31,19 @@ import org.apache.hadoop.hbase.classification.InterfaceStability;
@InterfaceAudience.Public
@InterfaceStability.Unstable
public interface AsyncAdmin {
+
+ /**
+ * @return Async Connection used by this object.
+ */
+ AsyncConnectionImpl getConnection();
+
+ /**
+ * @param tableName Table to check.
+ * @return True if table exists already. The return value will be wrapped by a
+ * {@link CompletableFuture}.
+ */
+ CompletableFuture<Boolean> tableExists(final TableName tableName);
+
/**
* List all the userspace tables.
* @return - returns an array of HTableDescriptors wrapped by a {@link CompletableFuture}.
@@ -83,11 +94,75 @@ public interface AsyncAdmin {
final boolean includeSysTables);
/**
- * @param tableName Table to check.
- * @return True if table exists already. The return value will be wrapped by a
- * {@link CompletableFuture}.
+ * Method for getting the tableDescriptor
+ * @param tableName as a {@link TableName}
+ * @return the tableDescriptor wrapped by a {@link CompletableFuture}.
*/
- CompletableFuture<Boolean> tableExists(final TableName tableName);
+ CompletableFuture<HTableDescriptor> getTableDescriptor(final TableName tableName);
+
+ /**
+ * Creates a new table.
+ * @param desc table descriptor for table
+ */
+ CompletableFuture<Void> createTable(HTableDescriptor desc);
+
+ /**
+ * Creates a new table with the specified number of regions. The start key specified will become
+ * the end key of the first region of the table, and the end key specified will become the start
+ * key of the last region of the table (the first region has a null start key and the last region
+ * has a null end key). BigInteger math will be used to divide the key range specified into enough
+ * segments to make the required number of total regions.
+ * @param desc table descriptor for table
+ * @param startKey beginning of key range
+ * @param endKey end of key range
+ * @param numRegions the total number of regions to create
+ */
+ CompletableFuture<Void> createTable(HTableDescriptor desc, byte[] startKey, byte[] endKey,
+ int numRegions);
+
+ /**
+ * Creates a new table with an initial set of empty regions defined by the specified split keys.
+ * The total number of regions created will be the number of split keys plus one.
+ * Note : Avoid passing empty split key.
+ * @param desc table descriptor for table
+ * @param splitKeys array of split keys for the initial regions of the table
+ */
+ CompletableFuture<Void> createTable(final HTableDescriptor desc, byte[][] splitKeys);
+
+ /**
+ * Deletes a table.
+ * @param tableName name of table to delete
+ */
+ CompletableFuture<Void> deleteTable(final TableName tableName);
+
+ /**
+ * Deletes tables matching the passed in pattern and wait on completion. Warning: Use this method
+ * carefully, there is no prompting and the effect is immediate. Consider using
+ * {@link #listTables(String, boolean)} and
+ * {@link #deleteTable(org.apache.hadoop.hbase.TableName)}
+ * @param regex The regular expression to match table names against
+ * @return Table descriptors for tables that couldn't be deleted. The return value will be wrapped
+ * by a {@link CompletableFuture}.
+ */
+ CompletableFuture<HTableDescriptor[]> deleteTables(String regex);
+
+ /**
+ * Delete tables matching the passed in pattern and wait on completion. Warning: Use this method
+ * carefully, there is no prompting and the effect is immediate. Consider using
+ * {@link #listTables(Pattern, boolean) } and
+ * {@link #deleteTable(org.apache.hadoop.hbase.TableName)}
+ * @param pattern The pattern to match table names against
+ * @return Table descriptors for tables that couldn't be deleted. The return value will be wrapped
+ * by a {@link CompletableFuture}.
+ */
+ CompletableFuture<HTableDescriptor[]> deleteTables(Pattern pattern);
+
+ /**
+ * Truncate a table.
+ * @param tableName name of table to truncate
+ * @param preserveSplits True if the splits should be preserved
+ */
+ CompletableFuture<Void> truncateTable(final TableName tableName, final boolean preserveSplits);
/**
* Turn the load balancer on or off.
http://git-wip-us.apache.org/repos/asf/hbase/blob/8aea84d7/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
index 8682116..5112b90 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
@@ -18,14 +18,21 @@
package org.apache.hadoop.hbase.client;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
import java.util.regex.Pattern;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.AsyncMetaTableAccessor;
-import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.classification.InterfaceStability;
import org.apache.hadoop.hbase.client.AsyncRpcRetryingCallerFactory.MasterRequestCallerBuilder;
@@ -33,17 +40,28 @@ import org.apache.hadoop.hbase.ipc.HBaseRpcController;
import org.apache.hadoop.hbase.shaded.com.google.protobuf.RpcCallback;
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.TableSchema;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.BalanceRequest;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.BalanceResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetProcedureResultRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetProcedureResultResponse;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetTableDescriptorsRequest;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetTableDescriptorsResponse;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetTableNamesRequest;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetTableNamesResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.CreateTableRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.CreateTableResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.DeleteTableRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.DeleteTableResponse;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsBalancerEnabledRequest;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsBalancerEnabledResponse;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MasterService;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.SetBalancerRunningRequest;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.SetBalancerRunningResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.TruncateTableRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.TruncateTableResponse;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.ForeignExceptionUtil;
/**
* The implementation of AsyncAdmin.
@@ -52,6 +70,8 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.SetBalance
@InterfaceStability.Evolving
public class AsyncHBaseAdmin implements AsyncAdmin {
+ private static final Log LOG = LogFactory.getLog(AsyncHBaseAdmin.class);
+
private final AsyncConnectionImpl connection;
private final long rpcTimeoutNs;
@@ -64,6 +84,8 @@ public class AsyncHBaseAdmin implements AsyncAdmin {
private final int startLogErrorsCnt;
+ private final NonceGenerator ng;
+
AsyncHBaseAdmin(AsyncConnectionImpl connection) {
this.connection = connection;
this.rpcTimeoutNs = connection.connConf.getRpcTimeoutNs();
@@ -71,6 +93,7 @@ public class AsyncHBaseAdmin implements AsyncAdmin {
this.pauseNs = connection.connConf.getPauseNs();
this.maxAttempts = connection.connConf.getMaxRetries();
this.startLogErrorsCnt = connection.connConf.getStartLogErrorsCnt();
+ this.ng = connection.getNonceGenerator();
}
private <T> MasterRequestCallerBuilder<T> newCaller() {
@@ -115,6 +138,16 @@ public class AsyncHBaseAdmin implements AsyncAdmin {
}
@Override
+ public AsyncConnectionImpl getConnection() {
+ return this.connection;
+ }
+
+ @Override
+ public CompletableFuture<Boolean> tableExists(TableName tableName) {
+ return AsyncMetaTableAccessor.tableExists(connection, tableName);
+ }
+
+ @Override
public CompletableFuture<HTableDescriptor[]> listTables() {
return listTables((Pattern)null, false);
}
@@ -159,6 +192,137 @@ public class AsyncHBaseAdmin implements AsyncAdmin {
}
@Override
+ public CompletableFuture<HTableDescriptor> getTableDescriptor(TableName tableName) {
+ CompletableFuture<HTableDescriptor> future = new CompletableFuture<>();
+ this.<List<TableSchema>> newCaller()
+ .action(
+ (controller, stub) -> this
+ .<GetTableDescriptorsRequest, GetTableDescriptorsResponse, List<TableSchema>> call(
+ controller, stub, RequestConverter.buildGetTableDescriptorsRequest(tableName), (s,
+ c, req, done) -> s.getTableDescriptors(c, req, done), (resp) -> resp
+ .getTableSchemaList())).call().whenComplete((tableSchemas, error) -> {
+ if (error != null) {
+ future.completeExceptionally(error);
+ return;
+ }
+ if (!tableSchemas.isEmpty()) {
+ future.complete(ProtobufUtil.convertToHTableDesc(tableSchemas.get(0)));
+ } else {
+ future.completeExceptionally(new TableNotFoundException(tableName.getNameAsString()));
+ }
+ });
+ return future;
+ }
+
+ @Override
+ public CompletableFuture<Void> createTable(HTableDescriptor desc) {
+ return createTable(desc, null);
+ }
+
+ @Override
+ public CompletableFuture<Void> createTable(HTableDescriptor desc, byte[] startKey, byte[] endKey,
+ int numRegions) {
+ try {
+ return createTable(desc, getSplitKeys(startKey, endKey, numRegions));
+ } catch (IllegalArgumentException e) {
+ return failedFuture(e);
+ }
+ }
+
+ @Override
+ public CompletableFuture<Void> createTable(HTableDescriptor desc, byte[][] splitKeys) {
+ if (desc.getTableName() == null) {
+ return failedFuture(new IllegalArgumentException("TableName cannot be null"));
+ }
+ if (splitKeys != null && splitKeys.length > 0) {
+ Arrays.sort(splitKeys, Bytes.BYTES_COMPARATOR);
+ // Verify there are no duplicate split keys
+ byte[] lastKey = null;
+ for (byte[] splitKey : splitKeys) {
+ if (Bytes.compareTo(splitKey, HConstants.EMPTY_BYTE_ARRAY) == 0) {
+ return failedFuture(new IllegalArgumentException(
+ "Empty split key must not be passed in the split keys."));
+ }
+ if (lastKey != null && Bytes.equals(splitKey, lastKey)) {
+ return failedFuture(new IllegalArgumentException("All split keys must be unique, "
+ + "found duplicate: " + Bytes.toStringBinary(splitKey) + ", "
+ + Bytes.toStringBinary(lastKey)));
+ }
+ lastKey = splitKey;
+ }
+ }
+
+ CompletableFuture<Long> procFuture = this
+ .<Long> newCaller()
+ .action(
+ (controller, stub) -> this.<CreateTableRequest, CreateTableResponse, Long> call(
+ controller,
+ stub,
+ RequestConverter.buildCreateTableRequest(desc, splitKeys, ng.getNonceGroup(),
+ ng.newNonce()), (s, c, req, done) -> s.createTable(c, req, done),
+ (resp) -> resp.getProcId())).call();
+ return waitProcedureResult(procFuture).whenComplete(
+ new CreateTableProcedureBiConsumer(this, desc.getTableName()));
+ }
+
+ @Override
+ public CompletableFuture<Void> deleteTable(TableName tableName) {
+ CompletableFuture<Long> procFuture = this
+ .<Long> newCaller()
+ .action(
+ (controller, stub) -> this.<DeleteTableRequest, DeleteTableResponse, Long> call(
+ controller, stub,
+ RequestConverter.buildDeleteTableRequest(tableName, ng.getNonceGroup(), ng.newNonce()),
+ (s, c, req, done) -> s.deleteTable(c, req, done), (resp) -> resp.getProcId())).call();
+ return waitProcedureResult(procFuture).whenComplete(
+ new DeleteTableProcedureBiConsumer(this, tableName));
+ }
+
+ @Override
+ public CompletableFuture<HTableDescriptor[]> deleteTables(String regex) {
+ return deleteTables(Pattern.compile(regex));
+ }
+
+ @Override
+ public CompletableFuture<HTableDescriptor[]> deleteTables(Pattern pattern) {
+ CompletableFuture<HTableDescriptor[]> future = new CompletableFuture<>();
+ List<HTableDescriptor> failed = new LinkedList<>();
+ listTables(pattern, false).whenComplete(
+ (tables, error) -> {
+ if (error != null) {
+ future.completeExceptionally(error);
+ return;
+ }
+ CompletableFuture[] futures = Arrays.stream(tables)
+ .map((table) -> deleteTable(table.getTableName()).whenComplete((v, ex) -> {
+ if (ex != null) {
+ LOG.info("Failed to delete table " + table.getTableName(), ex);
+ failed.add(table);
+ }
+ })).toArray(size -> new CompletableFuture[size]);
+ CompletableFuture.allOf(futures).thenAccept((v) -> {
+ future.complete(failed.toArray(new HTableDescriptor[failed.size()]));
+ });
+ });
+ return future;
+ }
+
+ @Override
+ public CompletableFuture<Void> truncateTable(TableName tableName, boolean preserveSplits) {
+ CompletableFuture<Long> procFuture = this
+ .<Long> newCaller()
+ .action(
+ (controller, stub) -> this.<TruncateTableRequest, TruncateTableResponse, Long> call(
+ controller,
+ stub,
+ RequestConverter.buildTruncateTableRequest(tableName, preserveSplits,
+ ng.getNonceGroup(), ng.newNonce()),
+ (s, c, req, done) -> s.truncateTable(c, req, done), (resp) -> resp.getProcId())).call();
+ return waitProcedureResult(procFuture).whenComplete(
+ new TruncateTableProcedureBiConsumer(this, tableName));
+ }
+
+ @Override
public CompletableFuture<Boolean> setBalancerRunning(final boolean on) {
return this
.<Boolean> newCaller()
@@ -196,8 +360,154 @@ public class AsyncHBaseAdmin implements AsyncAdmin {
.call();
}
- @Override
- public CompletableFuture<Boolean> tableExists(TableName tableName) {
- return AsyncMetaTableAccessor.tableExists(connection, tableName);
+ private byte[][] getSplitKeys(byte[] startKey, byte[] endKey, int numRegions) {
+ if (numRegions < 3) {
+ throw new IllegalArgumentException("Must create at least three regions");
+ } else if (Bytes.compareTo(startKey, endKey) >= 0) {
+ throw new IllegalArgumentException("Start key must be smaller than end key");
+ }
+ if (numRegions == 3) {
+ return new byte[][] { startKey, endKey };
+ }
+ byte[][] splitKeys = Bytes.split(startKey, endKey, numRegions - 3);
+ if (splitKeys == null || splitKeys.length != numRegions - 1) {
+ throw new IllegalArgumentException("Unable to split key range into enough regions");
+ }
+ return splitKeys;
+ }
+
+ private abstract class ProcedureBiConsumer implements BiConsumer<Void, Throwable> {
+ protected final AsyncAdmin admin;
+
+ ProcedureBiConsumer(AsyncAdmin admin) {
+ this.admin = admin;
+ }
+
+ abstract void onFinished();
+
+ abstract void onError(Throwable error);
+
+ @Override
+ public void accept(Void v, Throwable error) {
+ if (error != null) {
+ onError(error);
+ return;
+ }
+ onFinished();
+ }
+ }
+
+ private abstract class TableProcedureBiConsumer extends ProcedureBiConsumer {
+ protected final TableName tableName;
+
+ TableProcedureBiConsumer(final AsyncAdmin admin, final TableName tableName) {
+ super(admin);
+ this.tableName = tableName;
+ }
+
+ abstract String getOperationType();
+
+ String getDescription() {
+ return "Operation: " + getOperationType() + ", " + "Table Name: "
+ + tableName.getNameWithNamespaceInclAsString();
+ }
+
+ @Override
+ void onFinished() {
+ LOG.info(getDescription() + " completed");
+ }
+
+ @Override
+ void onError(Throwable error) {
+ LOG.info(getDescription() + " failed with " + error.getMessage());
+ }
+ }
+
+ private class CreateTableProcedureBiConsumer extends TableProcedureBiConsumer {
+
+ CreateTableProcedureBiConsumer(AsyncAdmin admin, TableName tableName) {
+ super(admin, tableName);
+ }
+
+ String getOperationType() {
+ return "CREATE";
+ }
+ }
+
+ private class DeleteTableProcedureBiConsumer extends TableProcedureBiConsumer {
+
+ DeleteTableProcedureBiConsumer(AsyncAdmin admin, TableName tableName) {
+ super(admin, tableName);
+ }
+
+ String getOperationType() {
+ return "DELETE";
+ }
+
+ @Override
+ void onFinished() {
+ this.admin.getConnection().getLocator().clearCache(this.tableName);
+ super.onFinished();
+ }
+ }
+
+ private class TruncateTableProcedureBiConsumer extends TableProcedureBiConsumer {
+
+ TruncateTableProcedureBiConsumer(AsyncAdmin admin, TableName tableName) {
+ super(admin, tableName);
+ }
+
+ String getOperationType() {
+ return "TRUNCATE";
+ }
+ }
+
+ private CompletableFuture<Void> waitProcedureResult(CompletableFuture<Long> procFuture) {
+ CompletableFuture<Void> future = new CompletableFuture<>();
+ procFuture.whenComplete((procId, error) -> {
+ if (error != null) {
+ future.completeExceptionally(error);
+ return;
+ }
+ getProcedureResult(procId, future);
+ });
+ return future;
+ }
+
+ private void getProcedureResult(final long procId, CompletableFuture<Void> future) {
+ this.<GetProcedureResultResponse> newCaller()
+ .action(
+ (controller, stub) -> this
+ .<GetProcedureResultRequest, GetProcedureResultResponse, GetProcedureResultResponse> call(
+ controller, stub, GetProcedureResultRequest.newBuilder().setProcId(procId).build(),
+ (s, c, req, done) -> s.getProcedureResult(c, req, done), (resp) -> resp))
+ .call()
+ .whenComplete(
+ (response, error) -> {
+ if (error != null) {
+ LOG.warn("failed to get the procedure result procId=" + procId,
+ ConnectionUtils.translateException(error));
+ connection.RETRY_TIMER.newTimeout(t -> getProcedureResult(procId, future), pauseNs,
+ TimeUnit.NANOSECONDS);
+ return;
+ }
+ if (response.getState() == GetProcedureResultResponse.State.RUNNING) {
+ connection.RETRY_TIMER.newTimeout(t -> getProcedureResult(procId, future), pauseNs,
+ TimeUnit.NANOSECONDS);
+ return;
+ }
+ if (response.hasException()) {
+ IOException ioe = ForeignExceptionUtil.toIOException(response.getException());
+ future.completeExceptionally(ioe);
+ } else {
+ future.complete(null);
+ }
+ });
+ }
+
+ private <T> CompletableFuture<T> failedFuture(Throwable error) {
+ CompletableFuture<T> future = new CompletableFuture<>();
+ future.completeExceptionally(error);
+ return future;
}
}
http://git-wip-us.apache.org/repos/asf/hbase/blob/8aea84d7/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncAdmin.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncAdmin.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncAdmin.java
index ccd6873..0835b47 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncAdmin.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncAdmin.java
@@ -17,22 +17,38 @@
*/
package org.apache.hadoop.hbase.client;
+import static org.apache.hadoop.hbase.client.AsyncProcess.START_LOG_ERRORS_AFTER_COUNT_KEY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletionException;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.AsyncMetaTableAccessor;
import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HRegionLocation;
import org.apache.hadoop.hbase.HTableDescriptor;
-import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.testclassification.ClientTests;
import org.apache.hadoop.hbase.testclassification.LargeTests;
import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.hadoop.hbase.util.Bytes;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -47,16 +63,18 @@ public class TestAsyncAdmin {
private static final Log LOG = LogFactory.getLog(TestAdmin1.class);
private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
- private static byte [] FAMILY = Bytes.toBytes("testFamily");
+ private static byte[] FAMILY = Bytes.toBytes("testFamily");
private static AsyncConnection ASYNC_CONN;
private AsyncAdmin admin;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
- TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 10);
- TEST_UTIL.getConfiguration().setInt("hbase.client.retries.number", 3);
- TEST_UTIL.getConfiguration().setInt("hbase.rpc.timeout", 1000);
+ TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_PAUSE, 10);
+ TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 3);
+ TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 1000);
+ TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_OPERATION_TIMEOUT, 3000);
+ TEST_UTIL.getConfiguration().setInt(START_LOG_ERRORS_AFTER_COUNT_KEY, 0);
TEST_UTIL.startMiniCluster(1);
ASYNC_CONN = ConnectionFactory.createAsyncConnection(TEST_UTIL.getConfiguration());
}
@@ -73,7 +91,21 @@ public class TestAsyncAdmin {
}
@Test
+ public void testTableExist() throws Exception {
+ final TableName table = TableName.valueOf("testTableExist");
+ boolean exist;
+ exist = admin.tableExists(table).get();
+ assertEquals(false, exist);
+ TEST_UTIL.createTable(table, FAMILY);
+ exist = admin.tableExists(table).get();
+ assertEquals(true, exist);
+ exist = admin.tableExists(TableName.META_TABLE_NAME).get();
+ assertEquals(true, exist);
+ }
+
+ @Test
public void testListTables() throws Exception {
+ int numTables = admin.listTables().get().length;
TableName t1 = TableName.valueOf("testListTables1");
TableName t2 = TableName.valueOf("testListTables2");
TableName t3 = TableName.valueOf("testListTables3");
@@ -98,7 +130,7 @@ public class TestAsyncAdmin {
TableName[] tableNames = admin.listTableNames().get();
size = tableNames.length;
- assertTrue(size >= tables.length);
+ assertTrue(size == (numTables + tables.length));
for (int i = 0; i < tables.length && i < size; i++) {
boolean found = false;
for (int j = 0; j < tableNames.length; j++) {
@@ -113,10 +145,6 @@ public class TestAsyncAdmin {
for (int i = 0; i < tables.length; i++) {
TEST_UTIL.deleteTable(tables[i]);
}
- tableDescs = admin.listTables().get();
- assertEquals(0, tableDescs.length);
- tableNames = admin.listTableNames().get();
- assertEquals(0, tableNames.length);
tableDescs = admin.listTables((Pattern) null, true).get();
assertTrue("Not found system tables", tableDescs.length > 0);
@@ -124,17 +152,372 @@ public class TestAsyncAdmin {
assertTrue("Not found system tables", tableNames.length > 0);
}
- @Test
- public void testTableExist() throws Exception {
- final TableName table = TableName.valueOf("testTableExist");
- boolean exist;
- exist = admin.tableExists(table).get();
- assertEquals(false, exist);
- TEST_UTIL.createTable(table, FAMILY);
- exist = admin.tableExists(table).get();
- assertEquals(true, exist);
- exist = admin.tableExists(TableName.META_TABLE_NAME).get();
- assertEquals(true, exist);
+ @Test(timeout = 300000)
+ public void testGetTableDescriptor() throws Exception {
+ HColumnDescriptor fam1 = new HColumnDescriptor("fam1");
+ HColumnDescriptor fam2 = new HColumnDescriptor("fam2");
+ HColumnDescriptor fam3 = new HColumnDescriptor("fam3");
+ HTableDescriptor htd = new HTableDescriptor(TableName.valueOf("myTestTable"));
+ htd.addFamily(fam1);
+ htd.addFamily(fam2);
+ htd.addFamily(fam3);
+ admin.createTable(htd).join();
+ HTableDescriptor confirmedHtd = admin.getTableDescriptor(htd.getTableName()).get();
+ assertEquals(htd.compareTo(confirmedHtd), 0);
+ }
+
+ @Test(timeout = 300000)
+ public void testCreateTable() throws Exception {
+ HTableDescriptor[] tables = admin.listTables().get();
+ int numTables = tables.length;
+ TableName tableName = TableName.valueOf("testCreateTable");
+ admin.createTable(new HTableDescriptor(tableName).addFamily(new HColumnDescriptor(FAMILY)))
+ .join();
+ tables = admin.listTables().get();
+ assertEquals(numTables + 1, tables.length);
+ assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster().getMaster()
+ .getTableStateManager().isTableState(tableName, TableState.State.ENABLED));
+ assertEquals(TableState.State.ENABLED, getStateFromMeta(tableName));
+ }
+
+ private TableState.State getStateFromMeta(TableName table) throws Exception {
+ Optional<TableState> state = AsyncMetaTableAccessor.getTableState(ASYNC_CONN, table).get();
+ assertTrue(state.isPresent());
+ return state.get().getState();
+ }
+
+ @Test(timeout = 300000)
+ public void testCreateTableNumberOfRegions() throws Exception {
+ TableName tableName = TableName.valueOf("testCreateTableNumberOfRegions");
+ HTableDescriptor desc = new HTableDescriptor(tableName);
+ desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
+ admin.createTable(desc).join();
+ List<HRegionLocation> regions;
+ try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
+ regions = l.getAllRegionLocations();
+ assertEquals("Table should have only 1 region", 1, regions.size());
+ }
+
+ TableName TABLE_2 = TableName.valueOf(tableName.getNameAsString() + "_2");
+ desc = new HTableDescriptor(TABLE_2);
+ desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
+ admin.createTable(desc, new byte[][] { new byte[] { 42 } }).join();
+ try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(TABLE_2)) {
+ regions = l.getAllRegionLocations();
+ assertEquals("Table should have only 2 region", 2, regions.size());
+ }
+
+ TableName TABLE_3 = TableName.valueOf(tableName.getNameAsString() + "_3");
+ desc = new HTableDescriptor(TABLE_3);
+ desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
+ admin.createTable(desc, "a".getBytes(), "z".getBytes(), 3).join();
+ try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(TABLE_3)) {
+ regions = l.getAllRegionLocations();
+ assertEquals("Table should have only 3 region", 3, regions.size());
+ }
+
+ TableName TABLE_4 = TableName.valueOf(tableName.getNameAsString() + "_4");
+ desc = new HTableDescriptor(TABLE_4);
+ desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
+ try {
+ admin.createTable(desc, "a".getBytes(), "z".getBytes(), 2).join();
+ fail("Should not be able to create a table with only 2 regions using this API.");
+ } catch (CompletionException e) {
+ assertTrue(e.getCause() instanceof IllegalArgumentException);
+ }
+
+ TableName TABLE_5 = TableName.valueOf(tableName.getNameAsString() + "_5");
+ desc = new HTableDescriptor(TABLE_5);
+ desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
+ admin.createTable(desc, new byte[] { 1 }, new byte[] { 127 }, 16).join();
+ try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(TABLE_5)) {
+ regions = l.getAllRegionLocations();
+ assertEquals("Table should have 16 region", 16, regions.size());
+ }
+ }
+
+ @Test(timeout = 300000)
+ public void testCreateTableWithRegions() throws IOException, InterruptedException {
+
+ TableName tableName = TableName.valueOf("testCreateTableWithRegions");
+
+ byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 }, new byte[] { 3, 3, 3 },
+ new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 }, new byte[] { 6, 6, 6 },
+ new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 }, new byte[] { 9, 9, 9 }, };
+ int expectedRegions = splitKeys.length + 1;
+
+ HTableDescriptor desc = new HTableDescriptor(tableName);
+ desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
+ admin.createTable(desc, splitKeys).join();
+
+ List<HRegionLocation> regions;
+ Iterator<HRegionLocation> hris;
+ HRegionInfo hri;
+ ClusterConnection conn = (ClusterConnection) TEST_UTIL.getConnection();
+ try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
+ regions = l.getAllRegionLocations();
+
+ assertEquals(
+ "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
+ expectedRegions, regions.size());
+ System.err.println("Found " + regions.size() + " regions");
+
+ hris = regions.iterator();
+ hri = hris.next().getRegionInfo();
+ assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
+ assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[0]));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[0]));
+ assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[1]));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[1]));
+ assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[2]));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[2]));
+ assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[3]));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[3]));
+ assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[4]));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[4]));
+ assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[5]));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[5]));
+ assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[6]));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[6]));
+ assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[7]));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[7]));
+ assertTrue(Bytes.equals(hri.getEndKey(), splitKeys[8]));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8]));
+ assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
+
+ verifyRoundRobinDistribution(conn, l, expectedRegions);
+ }
+
+ // Now test using start/end with a number of regions
+
+ // Use 80 bit numbers to make sure we aren't limited
+ byte[] startKey = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+ byte[] endKey = { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
+
+ // Splitting into 10 regions, we expect (null,1) ... (9, null)
+ // with (1,2) (2,3) (3,4) (4,5) (5,6) (6,7) (7,8) (8,9) in the middle
+
+ expectedRegions = 10;
+
+ TableName TABLE_2 = TableName.valueOf(tableName.getNameAsString() + "_2");
+
+ desc = new HTableDescriptor(TABLE_2);
+ desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
+ admin.createTable(desc, startKey, endKey, expectedRegions).join();
+
+ try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(TABLE_2)) {
+ regions = l.getAllRegionLocations();
+ assertEquals(
+ "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
+ expectedRegions, regions.size());
+ System.err.println("Found " + regions.size() + " regions");
+
+ hris = regions.iterator();
+ hri = hris.next().getRegionInfo();
+ assertTrue(hri.getStartKey() == null || hri.getStartKey().length == 0);
+ assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }));
+ assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }));
+ assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }));
+ assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }));
+ assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }));
+ assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }));
+ assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }));
+ assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }));
+ assertTrue(Bytes.equals(hri.getEndKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }));
+ hri = hris.next().getRegionInfo();
+ assertTrue(Bytes.equals(hri.getStartKey(), new byte[] { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }));
+ assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0);
+
+ verifyRoundRobinDistribution(conn, l, expectedRegions);
+ }
+
+ // Try once more with something that divides into something infinite
+
+ startKey = new byte[] { 0, 0, 0, 0, 0, 0 };
+ endKey = new byte[] { 1, 0, 0, 0, 0, 0 };
+
+ expectedRegions = 5;
+
+ TableName TABLE_3 = TableName.valueOf(tableName.getNameAsString() + "_3");
+
+ desc = new HTableDescriptor(TABLE_3);
+ desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
+ admin.createTable(desc, startKey, endKey, expectedRegions).join();
+
+ try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(TABLE_3)) {
+ regions = l.getAllRegionLocations();
+ assertEquals(
+ "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
+ expectedRegions, regions.size());
+ System.err.println("Found " + regions.size() + " regions");
+
+ verifyRoundRobinDistribution(conn, l, expectedRegions);
+ }
+
+ // Try an invalid case where there are duplicate split keys
+ splitKeys = new byte[][] { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 },
+ new byte[] { 3, 3, 3 }, new byte[] { 2, 2, 2 } };
+
+ TableName TABLE_4 = TableName.valueOf(tableName.getNameAsString() + "_4");
+ desc = new HTableDescriptor(TABLE_4);
+ desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
+ try {
+ admin.createTable(desc, splitKeys).join();
+ fail("Should not be able to create this table because of " + "duplicate split keys");
+ } catch (CompletionException e) {
+ assertTrue(e.getCause() instanceof IllegalArgumentException);
+ }
+ }
+
+ private void verifyRoundRobinDistribution(ClusterConnection c, RegionLocator regionLocator,
+ int expectedRegions) throws IOException {
+ int numRS = c.getCurrentNrHRS();
+ List<HRegionLocation> regions = regionLocator.getAllRegionLocations();
+ Map<ServerName, List<HRegionInfo>> server2Regions = new HashMap<>();
+ regions.stream().forEach((loc) -> {
+ ServerName server = loc.getServerName();
+ server2Regions.computeIfAbsent(server, (s) -> new ArrayList<>()).add(loc.getRegionInfo());
+ });
+ if (numRS >= 2) {
+ // Ignore the master region server,
+ // which contains less regions by intention.
+ numRS--;
+ }
+ float average = (float) expectedRegions / numRS;
+ int min = (int) Math.floor(average);
+ int max = (int) Math.ceil(average);
+ server2Regions.values().forEach((regionList) -> {
+ assertTrue(regionList.size() == min || regionList.size() == max);
+ });
+ }
+
+ @Test(timeout = 300000)
+ public void testCreateTableWithOnlyEmptyStartRow() throws IOException {
+ byte[] tableName = Bytes.toBytes("testCreateTableWithOnlyEmptyStartRow");
+ byte[][] splitKeys = new byte[1][];
+ splitKeys[0] = HConstants.EMPTY_BYTE_ARRAY;
+ HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
+ desc.addFamily(new HColumnDescriptor("col"));
+ try {
+ admin.createTable(desc, splitKeys).join();
+ fail("Test case should fail as empty split key is passed.");
+ } catch (CompletionException e) {
+ assertTrue(e.getCause() instanceof IllegalArgumentException);
+ }
+ }
+
+ @Test(timeout = 300000)
+ public void testCreateTableWithEmptyRowInTheSplitKeys() throws IOException {
+ byte[] tableName = Bytes.toBytes("testCreateTableWithEmptyRowInTheSplitKeys");
+ byte[][] splitKeys = new byte[3][];
+ splitKeys[0] = "region1".getBytes();
+ splitKeys[1] = HConstants.EMPTY_BYTE_ARRAY;
+ splitKeys[2] = "region2".getBytes();
+ HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
+ desc.addFamily(new HColumnDescriptor("col"));
+ try {
+ admin.createTable(desc, splitKeys).join();
+ fail("Test case should fail as empty split key is passed.");
+ } catch (CompletionException e) {
+ assertTrue(e.getCause() instanceof IllegalArgumentException);
+ }
+ }
+
+ @Test(timeout = 300000)
+ public void testDeleteTable() throws Exception {
+ TableName table = TableName.valueOf("testDeleteTable");
+ admin.createTable(new HTableDescriptor(table).addFamily(new HColumnDescriptor(FAMILY))).join();
+ assertTrue(admin.tableExists(table).get());
+ TEST_UTIL.getAdmin().disableTable(table);
+ admin.deleteTable(table).join();
+ assertFalse(admin.tableExists(table).get());
+ }
+
+ @Test(timeout = 300000)
+ public void testDeleteTables() throws Exception {
+ TableName[] tables = { TableName.valueOf("testDeleteTables1"),
+ TableName.valueOf("testDeleteTables2"), TableName.valueOf("testDeleteTables3") };
+ Arrays.stream(tables).map(HTableDescriptor::new)
+ .map((table) -> table.addFamily(new HColumnDescriptor(FAMILY))).forEach((table) -> {
+ admin.createTable(table).join();
+ admin.tableExists(table.getTableName()).thenAccept((exist) -> assertTrue(exist)).join();
+ try {
+ TEST_UTIL.getAdmin().disableTable(table.getTableName());
+ } catch (Exception e) {
+ }
+ });
+ HTableDescriptor[] failed = admin.deleteTables(Pattern.compile("testDeleteTables.*")).get();
+ assertEquals(0, failed.length);
+ Arrays.stream(tables).forEach((table) -> {
+ admin.tableExists(table).thenAccept((exist) -> assertFalse(exist)).join();
+ });
+ }
+
+ @Test(timeout = 300000)
+ public void testTruncateTable() throws IOException {
+ testTruncateTable(TableName.valueOf("testTruncateTable"), false);
+ }
+
+ @Test(timeout = 300000)
+ public void testTruncateTablePreservingSplits() throws IOException {
+ testTruncateTable(TableName.valueOf("testTruncateTablePreservingSplits"), true);
+ }
+
+ private void testTruncateTable(final TableName tableName, boolean preserveSplits)
+ throws IOException {
+ byte[][] splitKeys = new byte[2][];
+ splitKeys[0] = Bytes.toBytes(4);
+ splitKeys[1] = Bytes.toBytes(8);
+
+ // Create & Fill the table
+ Table table = TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY, splitKeys);
+ try {
+ TEST_UTIL.loadNumericRows(table, HConstants.CATALOG_FAMILY, 0, 10);
+ assertEquals(10, TEST_UTIL.countRows(table));
+ } finally {
+ table.close();
+ }
+ assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
+
+ // Truncate & Verify
+ TEST_UTIL.getAdmin().disableTable(tableName);
+ admin.truncateTable(tableName, preserveSplits).join();
+ table = TEST_UTIL.getConnection().getTable(tableName);
+ try {
+ assertEquals(0, TEST_UTIL.countRows(table));
+ } finally {
+ table.close();
+ }
+ if (preserveSplits) {
+ assertEquals(3, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
+ } else {
+ assertEquals(1, TEST_UTIL.getHBaseCluster().getRegions(tableName).size());
+ }
}
@Test(timeout = 30000)