Repository: hbase Updated Branches: refs/heads/master 91995749c -> ff998ef74
HBASE-17867: Implement async procedure RPC API(list/exec/abort/isFinished) Signed-off-by: Guanghao Zhang <zg...@apache.org> Project: http://git-wip-us.apache.org/repos/asf/hbase/repo Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/ff998ef7 Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/ff998ef7 Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/ff998ef7 Branch: refs/heads/master Commit: ff998ef74fe7b8d304b2e9e5579b019c62f836db Parents: 9199574 Author: huzheng <open...@gmail.com> Authored: Tue May 2 15:56:27 2017 +0800 Committer: Guanghao Zhang <zg...@apache.org> Committed: Wed May 3 21:46:37 2017 +0800 ---------------------------------------------------------------------- .../apache/hadoop/hbase/client/AsyncAdmin.java | 55 +++++++++ .../hadoop/hbase/client/AsyncHBaseAdmin.java | 110 +++++++++++++++++ .../apache/hadoop/hbase/client/HBaseAdmin.java | 63 ++++------ .../hbase/shaded/protobuf/ProtobufUtil.java | 12 ++ .../client/TestAsyncProcedureAdminApi.java | 118 +++++++++++++++++++ 5 files changed, 316 insertions(+), 42 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hbase/blob/ff998ef7/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 0782f5a..b764726 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 @@ -25,6 +25,7 @@ import java.util.regex.Pattern; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.ProcedureInfo; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.TableName; @@ -741,4 +742,58 @@ public interface AsyncAdmin { */ CompletableFuture<Void> deleteTableSnapshots(Pattern tableNamePattern, Pattern snapshotNamePattern); + + /** + * Execute a distributed procedure on a cluster. + * @param signature A distributed procedure is uniquely identified by its signature (default the + * root ZK node name of the procedure). + * @param instance The instance name of the procedure. For some procedures, this parameter is + * optional. + * @param props Property/Value pairs of properties passing to the procedure + */ + CompletableFuture<Void> execProcedure(String signature, String instance, + Map<String, String> props); + + /** + * Execute a distributed procedure on a cluster. + * @param signature A distributed procedure is uniquely identified by its signature (default the + * root ZK node name of the procedure). + * @param instance The instance name of the procedure. For some procedures, this parameter is + * optional. + * @param props Property/Value pairs of properties passing to the procedure + * @return data returned after procedure execution. null if no return data. + */ + CompletableFuture<byte[]> execProcedureWithRet(String signature, String instance, + Map<String, String> props); + + /** + * Check the current state of the specified procedure. There are three possible states: + * <ol> + * <li>running - returns <tt>false</tt></li> + * <li>finished - returns <tt>true</tt></li> + * <li>finished with error - throws the exception that caused the procedure to fail</li> + * </ol> + * @param signature The signature that uniquely identifies a procedure + * @param instance The instance name of the procedure + * @param props Property/Value pairs of properties passing to the procedure + * @return true if the specified procedure is finished successfully, false if it is still running. + * The value is vrapped by {@link CompletableFuture} + */ + CompletableFuture<Boolean> isProcedureFinished(String signature, String instance, + Map<String, String> props); + + /** + * abort a procedure + * @param procId ID of the procedure to abort + * @param mayInterruptIfRunning if the proc completed at least one step, should it be aborted? + * @return true if aborted, false if procedure already completed or does not exist. the value is + * wrapped by {@link CompletableFuture} + */ + CompletableFuture<Boolean> abortProcedure(long procId, boolean mayInterruptIfRunning); + + /** + * List procedures + * @return procedure list wrapped by {@link CompletableFuture} + */ + CompletableFuture<ProcedureInfo[]> listProcedures(); } http://git-wip-us.apache.org/repos/asf/hbase/blob/ff998ef7/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 daa97bc..019d0c6 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 @@ -48,6 +48,7 @@ import org.apache.hadoop.hbase.HRegionLocation; import org.apache.hadoop.hbase.MetaTableAccessor; import org.apache.hadoop.hbase.MetaTableAccessor.QueryType; import org.apache.hadoop.hbase.NotServingRegionException; +import org.apache.hadoop.hbase.ProcedureInfo; import org.apache.hadoop.hbase.RegionLocations; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.NamespaceDescriptor; @@ -82,7 +83,11 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CloseRegion import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.SplitRegionRequest; import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.SplitRegionResponse; import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; +import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.NameStringPair; +import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.ProcedureDescription; import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.TableSchema; +import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.AbortProcedureRequest; +import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.AbortProcedureResponse; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.AddColumnRequest; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.AddColumnResponse; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.AssignRegionRequest; @@ -101,6 +106,8 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.EnableTabl import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.EnableTableResponse; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.DeleteColumnRequest; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.DeleteColumnResponse; +import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.ExecProcedureRequest; +import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.ExecProcedureResponse; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetCompletedSnapshotsRequest; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetCompletedSnapshotsResponse; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.GetNamespaceDescriptorRequest; @@ -119,10 +126,14 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.DeleteTabl 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.IsProcedureDoneRequest; +import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsProcedureDoneResponse; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsSnapshotDoneRequest; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.IsSnapshotDoneResponse; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.ListNamespaceDescriptorsRequest; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.ListNamespaceDescriptorsResponse; +import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.ListProceduresRequest; +import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.ListProceduresResponse; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MasterService; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MergeTableRegionsRequest; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProtos.MergeTableRegionsResponse; @@ -1764,6 +1775,105 @@ public class AsyncHBaseAdmin implements AsyncAdmin { return future; } + @Override + public CompletableFuture<Void> execProcedure(String signature, String instance, + Map<String, String> props) { + CompletableFuture<Void> future = new CompletableFuture<>(); + ProcedureDescription procDesc = + ProtobufUtil.buildProcedureDescription(signature, instance, props); + this.<Long> newMasterCaller() + .action((controller, stub) -> this.<ExecProcedureRequest, ExecProcedureResponse, Long> call( + controller, stub, ExecProcedureRequest.newBuilder().setProcedure(procDesc).build(), + (s, c, req, done) -> s.execProcedure(c, req, done), resp -> resp.getExpectedTimeout())) + .call().whenComplete((expectedTimeout, err) -> { + if (err != null) { + future.completeExceptionally(err); + return; + } + TimerTask pollingTask = new TimerTask() { + int tries = 0; + long startTime = EnvironmentEdgeManager.currentTime(); + long endTime = startTime + expectedTimeout; + long maxPauseTime = expectedTimeout / maxAttempts; + + @Override + public void run(Timeout timeout) throws Exception { + if (EnvironmentEdgeManager.currentTime() < endTime) { + isProcedureFinished(signature, instance, props).whenComplete((done, err) -> { + if (err != null) { + future.completeExceptionally(err); + return; + } + if (done) { + future.complete(null); + } else { + // retry again after pauseTime. + long pauseTime = ConnectionUtils + .getPauseTime(TimeUnit.NANOSECONDS.toMillis(pauseNs), ++tries); + pauseTime = Math.min(pauseTime, maxPauseTime); + AsyncConnectionImpl.RETRY_TIMER.newTimeout(this, pauseTime, + TimeUnit.MICROSECONDS); + } + }); + } else { + future.completeExceptionally(new IOException("Procedure '" + signature + " : " + + instance + "' wasn't completed in expectedTime:" + expectedTimeout + " ms")); + } + } + }; + // Queue the polling task into RETRY_TIMER to poll procedure state asynchronously. + AsyncConnectionImpl.RETRY_TIMER.newTimeout(pollingTask, 1, TimeUnit.MILLISECONDS); + }); + return future; + } + + @Override + public CompletableFuture<byte[]> execProcedureWithRet(String signature, String instance, + Map<String, String> props) { + ProcedureDescription proDesc = + ProtobufUtil.buildProcedureDescription(signature, instance, props); + return this.<byte[]> newMasterCaller() + .action( + (controller, stub) -> this.<ExecProcedureRequest, ExecProcedureResponse, byte[]> call( + controller, stub, ExecProcedureRequest.newBuilder().setProcedure(proDesc).build(), + (s, c, req, done) -> s.execProcedureWithRet(c, req, done), + resp -> resp.hasReturnData() ? resp.getReturnData().toByteArray() : null)) + .call(); + } + + @Override + public CompletableFuture<Boolean> isProcedureFinished(String signature, String instance, + Map<String, String> props) { + ProcedureDescription proDesc = + ProtobufUtil.buildProcedureDescription(signature, instance, props); + return this.<Boolean> newMasterCaller() + .action((controller, stub) -> this + .<IsProcedureDoneRequest, IsProcedureDoneResponse, Boolean> call(controller, stub, + IsProcedureDoneRequest.newBuilder().setProcedure(proDesc).build(), + (s, c, req, done) -> s.isProcedureDone(c, req, done), resp -> resp.getDone())) + .call(); + } + + @Override + public CompletableFuture<Boolean> abortProcedure(long procId, boolean mayInterruptIfRunning) { + return this.<Boolean> newMasterCaller().action( + (controller, stub) -> this.<AbortProcedureRequest, AbortProcedureResponse, Boolean> call( + controller, stub, AbortProcedureRequest.newBuilder().setProcId(procId).build(), + (s, c, req, done) -> s.abortProcedure(c, req, done), resp -> resp.getIsProcedureAborted())) + .call(); + } + + @Override + public CompletableFuture<ProcedureInfo[]> listProcedures() { + return this.<ProcedureInfo[]> newMasterCaller() + .action((controller, stub) -> this + .<ListProceduresRequest, ListProceduresResponse, ProcedureInfo[]> call(controller, stub, + ListProceduresRequest.newBuilder().build(), + (s, c, req, done) -> s.listProcedures(c, req, done), resp -> resp.getProcedureList() + .stream().map(ProtobufUtil::toProcedureInfo).toArray(ProcedureInfo[]::new))) + .call(); + } + private CompletableFuture<Void> internalDeleteSnapshot(SnapshotDescription snapshot) { return this.<Void> newMasterCaller() .action((controller, stub) -> this http://git-wip-us.apache.org/repos/asf/hbase/blob/ff998ef7/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java index 7e90ff3..0c62688 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java @@ -2657,24 +2657,17 @@ public class HBaseAdmin implements Admin { @Override public byte[] execProcedureWithRet(String signature, String instance, Map<String, String> props) throws IOException { - ProcedureDescription.Builder builder = ProcedureDescription.newBuilder(); - builder.setSignature(signature).setInstance(instance); - for (Entry<String, String> entry : props.entrySet()) { - NameStringPair pair = NameStringPair.newBuilder().setName(entry.getKey()) - .setValue(entry.getValue()).build(); - builder.addConfiguration(pair); - } - - final ExecProcedureRequest request = ExecProcedureRequest.newBuilder() - .setProcedure(builder.build()).build(); + ProcedureDescription desc = ProtobufUtil.buildProcedureDescription(signature, instance, props); + final ExecProcedureRequest request = + ExecProcedureRequest.newBuilder().setProcedure(desc).build(); // run the procedure on the master - ExecProcedureResponse response = executeCallable(new MasterCallable<ExecProcedureResponse>( - getConnection(), getRpcControllerFactory()) { - @Override - protected ExecProcedureResponse rpcCall() throws Exception { - return master.execProcedureWithRet(getRpcController(), request); - } - }); + ExecProcedureResponse response = executeCallable( + new MasterCallable<ExecProcedureResponse>(getConnection(), getRpcControllerFactory()) { + @Override + protected ExecProcedureResponse rpcCall() throws Exception { + return master.execProcedureWithRet(getRpcController(), request); + } + }); return response.hasReturnData() ? response.getReturnData().toByteArray() : null; } @@ -2682,16 +2675,9 @@ public class HBaseAdmin implements Admin { @Override public void execProcedure(String signature, String instance, Map<String, String> props) throws IOException { - ProcedureDescription.Builder builder = ProcedureDescription.newBuilder(); - builder.setSignature(signature).setInstance(instance); - for (Entry<String, String> entry : props.entrySet()) { - NameStringPair pair = NameStringPair.newBuilder().setName(entry.getKey()) - .setValue(entry.getValue()).build(); - builder.addConfiguration(pair); - } - - final ExecProcedureRequest request = ExecProcedureRequest.newBuilder() - .setProcedure(builder.build()).build(); + ProcedureDescription desc = ProtobufUtil.buildProcedureDescription(signature, instance, props); + final ExecProcedureRequest request = + ExecProcedureRequest.newBuilder().setProcedure(desc).build(); // run the procedure on the master ExecProcedureResponse response = executeCallable(new MasterCallable<ExecProcedureResponse>( getConnection(), getRpcControllerFactory()) { @@ -2732,22 +2718,15 @@ public class HBaseAdmin implements Admin { @Override public boolean isProcedureFinished(String signature, String instance, Map<String, String> props) throws IOException { - final ProcedureDescription.Builder builder = ProcedureDescription.newBuilder(); - builder.setSignature(signature).setInstance(instance); - for (Entry<String, String> entry : props.entrySet()) { - NameStringPair pair = NameStringPair.newBuilder().setName(entry.getKey()) - .setValue(entry.getValue()).build(); - builder.addConfiguration(pair); - } - final ProcedureDescription desc = builder.build(); + ProcedureDescription desc = ProtobufUtil.buildProcedureDescription(signature, instance, props); return executeCallable( - new MasterCallable<IsProcedureDoneResponse>(getConnection(), getRpcControllerFactory()) { - @Override - protected IsProcedureDoneResponse rpcCall() throws Exception { - return master.isProcedureDone(getRpcController(), IsProcedureDoneRequest - .newBuilder().setProcedure(desc).build()); - } - }).getDone(); + new MasterCallable<IsProcedureDoneResponse>(getConnection(), getRpcControllerFactory()) { + @Override + protected IsProcedureDoneResponse rpcCall() throws Exception { + return master.isProcedureDone(getRpcController(), + IsProcedureDoneRequest.newBuilder().setProcedure(desc).build()); + } + }).getDone(); } /** http://git-wip-us.apache.org/repos/asf/hbase/blob/ff998ef7/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java index d4c4231..ff576f7 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java @@ -148,6 +148,7 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.BytesBytesP import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.ColumnFamilySchema; import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.NameBytesPair; import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.NameStringPair; +import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.ProcedureDescription; import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.RegionInfo; import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.RegionSpecifier; import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType; @@ -3267,6 +3268,17 @@ public final class ProtobufUtil { return builder.build(); } + public static ProcedureDescription buildProcedureDescription(String signature, String instance, + Map<String, String> props) { + ProcedureDescription.Builder builder = + ProcedureDescription.newBuilder().setSignature(signature).setInstance(instance); + if (props != null && !props.isEmpty()) { + props.entrySet().forEach(entry -> builder.addConfiguration( + NameStringPair.newBuilder().setName(entry.getKey()).setValue(entry.getValue()).build())); + } + return builder.build(); + } + /** * Get a ServerName from the passed in data bytes. * @param data Data with a serialize server name in it; can handle the old style http://git-wip-us.apache.org/repos/asf/hbase/blob/ff998ef7/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncProcedureAdminApi.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncProcedureAdminApi.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncProcedureAdminApi.java new file mode 100644 index 0000000..b740394 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncProcedureAdminApi.java @@ -0,0 +1,118 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hbase.client; + +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.ProcedureInfo; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.master.snapshot.SnapshotManager; +import org.apache.hadoop.hbase.procedure.ProcedureManagerHost; +import org.apache.hadoop.hbase.procedure.SimpleMasterProcedureManager; +import org.apache.hadoop.hbase.procedure.SimpleRSProcedureManager; +import org.apache.hadoop.hbase.testclassification.ClientTests; +import org.apache.hadoop.hbase.testclassification.MediumTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Class to test asynchronous procedure admin operations. + */ +@Category({ MediumTests.class, ClientTests.class }) +public class TestAsyncProcedureAdminApi extends TestAsyncAdminBase { + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + 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().set(ProcedureManagerHost.MASTER_PROCEDURE_CONF_KEY, + SimpleMasterProcedureManager.class.getName()); + TEST_UTIL.getConfiguration().set(ProcedureManagerHost.REGIONSERVER_PROCEDURE_CONF_KEY, + SimpleRSProcedureManager.class.getName()); + TEST_UTIL.getConfiguration().setBoolean(SnapshotManager.HBASE_SNAPSHOT_ENABLED, true); + TEST_UTIL.startMiniCluster(2); + ASYNC_CONN = ConnectionFactory.createAsyncConnection(TEST_UTIL.getConfiguration()).get(); + } + + @Test + public void testExecProcedure() throws Exception { + TableName tableName = TableName.valueOf("testExecProcedure"); + try { + Table table = TEST_UTIL.createTable(tableName, Bytes.toBytes("cf")); + for (int i = 0; i < 100; i++) { + Put put = new Put(Bytes.toBytes(i)).addColumn(Bytes.toBytes("cf"), null, Bytes.toBytes(i)); + table.put(put); + } + // take a snapshot of the enabled table + String snapshotString = "offlineTableSnapshot"; + Map<String, String> props = new HashMap<>(); + props.put("table", tableName.getNameAsString()); + admin.execProcedure(SnapshotManager.ONLINE_SNAPSHOT_CONTROLLER_DESCRIPTION, snapshotString, + props).get(); + LOG.debug("Snapshot completed."); + } finally { + TEST_UTIL.deleteTable(tableName); + } + } + + @Test + public void testExecProcedureWithRet() throws Exception { + byte[] result = admin.execProcedureWithRet(SimpleMasterProcedureManager.SIMPLE_SIGNATURE, + "myTest2", new HashMap<>()).get(); + assertArrayEquals("Incorrect return data from execProcedure", + SimpleMasterProcedureManager.SIMPLE_DATA.getBytes(), result); + } + + @Test + public void listProcedure() throws Exception { + ProcedureInfo[] procList = admin.listProcedures().get(); + assertTrue(procList.length >= 0); + } + + @Test + public void isProcedureFinished() throws Exception { + boolean failed = false; + try { + admin.isProcedureFinished("fake-signature", "fake-instance", new HashMap<>()).get(); + } catch (Exception e) { + failed = true; + } + Assert.assertTrue(failed); + } + + @Test + public void abortProcedure() throws Exception { + Random randomGenerator = new Random(); + long procId = randomGenerator.nextLong(); + boolean abortResult = admin.abortProcedure(procId, true).get(); + assertFalse(abortResult); + } +} \ No newline at end of file