All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.hbase.client.HBaseAdmin Maven / Gradle / Ivy

The newest version!
/**
 *
 * 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 com.google.common.annotations.VisibleForTesting;
import com.google.protobuf.ByteString;
import com.google.protobuf.ServiceException;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Abortable;
import org.apache.hadoop.hbase.ClusterStatus;
import org.apache.hadoop.hbase.DoNotRetryIOException;
import org.apache.hadoop.hbase.HBaseConfiguration;
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.MasterNotRunningException;
import org.apache.hadoop.hbase.MetaTableAccessor;
import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.NamespaceNotFoundException;
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.TableExistsException;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNotDisabledException;
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.UnknownRegionException;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.classification.InterfaceStability;
import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
import org.apache.hadoop.hbase.client.security.SecurityCapability;
import org.apache.hadoop.hbase.exceptions.DeserializationException;
import org.apache.hadoop.hbase.exceptions.TimeoutIOException;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
import org.apache.hadoop.hbase.ipc.MasterCoprocessorRpcChannel;
import org.apache.hadoop.hbase.ipc.RegionServerCoprocessorRpcChannel;
import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.protobuf.RequestConverter;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CloseRegionRequest;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CloseRegionResponse;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CompactRegionRequest;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CompactionSwitchRequest;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CompactionSwitchResponse;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.FlushRegionRequest;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoRequest;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoResponse;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoResponse.CompactionState;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.RollWALWriterRequest;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.RollWALWriterResponse;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.StopServerRequest;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.UpdateConfigurationRequest;
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair;
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ProcedureDescription;
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType;
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableSchema;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AbortProcedureRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AbortProcedureResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AddColumnRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AssignRegionRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ClearDeadServersRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateNamespaceRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateTableRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateTableResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteColumnRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteNamespaceRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteSnapshotRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteTableRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteTableResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DisableTableRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DisableTableResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DispatchMergingRegionsRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.EnableTableRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.EnableTableResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ExecProcedureRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ExecProcedureResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetClusterStatusRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetCompletedSnapshotsRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetNamespaceDescriptorRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetProcedureResultRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetProcedureResultResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetSchemaAlterStatusRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetSchemaAlterStatusResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableNamesRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsInMaintenanceModeRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsInMaintenanceModeResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsProcedureDoneRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsProcedureDoneResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsRestoreSnapshotDoneRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsRestoreSnapshotDoneResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotCleanupEnabledRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListNamespaceDescriptorsRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListNamespacesRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListProceduresRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListTableDescriptorsByNamespaceRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListTableNamesByNamespaceRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MajorCompactionTimestampForRegionRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MajorCompactionTimestampRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyColumnRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyNamespaceRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyTableRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MoveRegionRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RestoreSnapshotRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RestoreSnapshotResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SecurityCapabilitiesRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetBalancerRunningRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetNormalizerRunningRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetSnapshotCleanupRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ShutdownRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotResponse;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.StopMasterRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.TruncateTableRequest;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.UnassignRegionRequest;
import org.apache.hadoop.hbase.protobuf.generated.ProcedureProtos;
import org.apache.hadoop.hbase.protobuf.generated.TableProtos;
import org.apache.hadoop.hbase.quotas.QuotaFilter;
import org.apache.hadoop.hbase.quotas.QuotaRetriever;
import org.apache.hadoop.hbase.quotas.QuotaSettings;
import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils;
import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
import org.apache.hadoop.hbase.snapshot.SnapshotCreationException;
import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException;
import org.apache.hadoop.hbase.util.Addressing;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.apache.hadoop.hbase.util.ForeignExceptionUtil;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
import org.apache.hadoop.ipc.RemoteException;
import org.apache.hadoop.util.StringUtils;
import org.apache.zookeeper.KeeperException;

/**
 * HBaseAdmin is no longer a client API. It is marked InterfaceAudience.Private indicating that
 * this is an HBase-internal class as defined in
 * https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/InterfaceClassification.html
 * There are no guarantees for backwards source / binary compatibility and methods or class can
 * change or go away without deprecation.
 * Use {@link Connection#getAdmin()} to obtain an instance of {@link Admin} instead of constructing
 * an HBaseAdmin directly.
 *
 * 

Connection should be an unmanaged connection obtained via * {@link ConnectionFactory#createConnection(Configuration)} * * @see ConnectionFactory * @see Connection * @see Admin */ @InterfaceAudience.Private @InterfaceStability.Evolving public class HBaseAdmin implements Admin { private static final Log LOG = LogFactory.getLog(HBaseAdmin.class); private static final String ZK_IDENTIFIER_PREFIX = "hbase-admin-on-"; private ClusterConnection connection; private volatile Configuration conf; private final long pause; private final int numRetries; // Some operations can take a long time such as disable of big table. // numRetries is for 'normal' stuff... Multiply by this factor when // want to wait a long time. private final int retryLongerMultiplier; private final int syncWaitTimeout; private boolean aborted; private boolean cleanupConnectionOnClose = false; // close the connection in close() private boolean closed = false; private int operationTimeout; private int rpcTimeout; private RpcRetryingCallerFactory rpcCallerFactory; private RpcControllerFactory rpcControllerFactory; private NonceGenerator ng; /** * Constructor. * See {@link #HBaseAdmin(Connection connection)} * * @param c Configuration object. Copied internally. * @deprecated Constructing HBaseAdmin objects manually has been deprecated. * Use {@link Connection#getAdmin()} to obtain an instance of {@link Admin} instead. */ @Deprecated public HBaseAdmin(Configuration c) throws MasterNotRunningException, ZooKeeperConnectionException, IOException { // Will not leak connections, as the new implementation of the constructor // does not throw exceptions anymore. this(ConnectionManager.getConnectionInternal(new Configuration(c))); this.cleanupConnectionOnClose = true; } @Override public int getOperationTimeout() { return operationTimeout; } /** * Constructor for externally managed Connections. * The connection to master will be created when required by admin functions. * * @param connection The Connection instance to use * @throws MasterNotRunningException * @throws ZooKeeperConnectionException are not * thrown anymore but kept into the interface for backward api compatibility * @deprecated Constructing HBaseAdmin objects manually has been deprecated. * Use {@link Connection#getAdmin()} to obtain an instance of {@link Admin} instead. */ @Deprecated public HBaseAdmin(Connection connection) throws MasterNotRunningException, ZooKeeperConnectionException { this((ClusterConnection)connection); } HBaseAdmin(ClusterConnection connection) { this.conf = connection.getConfiguration(); this.connection = connection; // TODO: receive ConnectionConfiguration here rather than re-parsing these configs every time. this.pause = this.conf.getLong(HConstants.HBASE_CLIENT_PAUSE, HConstants.DEFAULT_HBASE_CLIENT_PAUSE); this.numRetries = this.conf.getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER); this.retryLongerMultiplier = this.conf.getInt( "hbase.client.retries.longer.multiplier", 10); this.operationTimeout = this.conf.getInt(HConstants.HBASE_CLIENT_OPERATION_TIMEOUT, HConstants.DEFAULT_HBASE_CLIENT_OPERATION_TIMEOUT); this.rpcTimeout = this.conf.getInt(HConstants.HBASE_RPC_TIMEOUT_KEY, HConstants.DEFAULT_HBASE_RPC_TIMEOUT); this.syncWaitTimeout = this.conf.getInt( "hbase.client.sync.wait.timeout.msec", 10 * 60000); // 10min this.rpcCallerFactory = connection.getRpcRetryingCallerFactory(); this.rpcControllerFactory = connection.getRpcControllerFactory(); this.ng = this.connection.getNonceGenerator(); } @Override public void abort(String why, Throwable e) { // Currently does nothing but throw the passed message and exception this.aborted = true; throw new RuntimeException(why, e); } @Override public boolean isAborted(){ return this.aborted; } /** * 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 * @throws IOException if a remote or network exception occurs */ @Override public boolean abortProcedure( final long procId, final boolean mayInterruptIfRunning) throws IOException { Future future = abortProcedureAsync(procId, mayInterruptIfRunning); try { return future.get(syncWaitTimeout, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { throw new InterruptedIOException("Interrupted when waiting for procedure to be cancelled"); } catch (TimeoutException e) { throw new TimeoutIOException(e); } catch (ExecutionException e) { if (e.getCause() instanceof IOException) { throw (IOException)e.getCause(); } else { throw new IOException(e.getCause()); } } } /** * Abort a procedure but does not block and wait for it be completely removed. * You can use Future.get(long, TimeUnit) to wait on the operation to complete. * It may throw ExecutionException if there was an error while executing the operation * or TimeoutException in case the wait timeout was not long enough to allow the * operation to complete. * * @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 * @throws IOException if a remote or network exception occurs */ @Override public Future abortProcedureAsync( final long procId, final boolean mayInterruptIfRunning) throws IOException { Boolean abortProcResponse = executeCallable( new MasterCallable(getConnection()) { @Override public AbortProcedureResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); AbortProcedureRequest abortProcRequest = AbortProcedureRequest.newBuilder().setProcId(procId).build(); return master.abortProcedure(controller, abortProcRequest); } }).getIsProcedureAborted(); AbortProcedureFuture abortProcFuture = new AbortProcedureFuture(this, procId, abortProcResponse); return abortProcFuture; } private static class AbortProcedureFuture extends ProcedureFuture { private boolean isAbortInProgress; public AbortProcedureFuture( final HBaseAdmin admin, final Long procId, final Boolean abortProcResponse) { super(admin, procId); this.isAbortInProgress = abortProcResponse; } @Override public Boolean get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (!this.isAbortInProgress) { return false; } super.get(timeout, unit); return true; } } /** @return HConnection used by this object. */ @Override public HConnection getConnection() { return connection; } /** @return - true if the master server is running. Throws an exception * otherwise. * @throws ZooKeeperConnectionException * @throws MasterNotRunningException * @deprecated this has been deprecated without a replacement */ @Deprecated public boolean isMasterRunning() throws MasterNotRunningException, ZooKeeperConnectionException { return connection.isMasterRunning(); } /** * @param tableName Table to check. * @return True if table exists already. * @throws IOException if a remote or network exception occurs */ @Override public boolean tableExists(final TableName tableName) throws IOException { return MetaTableAccessor.tableExists(connection, tableName); } public boolean tableExists(final byte[] tableName) throws IOException { return tableExists(TableName.valueOf(tableName)); } public boolean tableExists(final String tableName) throws IOException { return tableExists(TableName.valueOf(tableName)); } @Override public HTableDescriptor[] listTables() throws IOException { return listTables((Pattern)null, false); } @Override public HTableDescriptor[] listTables(Pattern pattern) throws IOException { return listTables(pattern, false); } @Override public HTableDescriptor[] listTables(String regex) throws IOException { return listTables(Pattern.compile(regex), false); } @Override public HTableDescriptor[] listTables(final Pattern pattern, final boolean includeSysTables) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public HTableDescriptor[] call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); GetTableDescriptorsRequest req = RequestConverter.buildGetTableDescriptorsRequest(pattern, includeSysTables); return ProtobufUtil.getHTableDescriptorArray(master.getTableDescriptors(controller, req)); } }); } @Override public HTableDescriptor[] listTables(String regex, boolean includeSysTables) throws IOException { return listTables(Pattern.compile(regex), includeSysTables); } /** * List all of the names of userspace tables. * @return String[] table names * @throws IOException if a remote or network exception occurs * @deprecated Use {@link Admin#listTableNames()} instead */ @Deprecated public String[] getTableNames() throws IOException { TableName[] tableNames = listTableNames(); String result[] = new String[tableNames.length]; for (int i = 0; i < tableNames.length; i++) { result[i] = tableNames[i].getNameAsString(); } return result; } /** * List all of the names of userspace tables matching the given regular expression. * @param pattern The regular expression to match against * @return String[] table names * @throws IOException if a remote or network exception occurs * @deprecated Use {@link Admin#listTableNames(Pattern)} instead. */ @Deprecated public String[] getTableNames(Pattern pattern) throws IOException { TableName[] tableNames = listTableNames(pattern); String result[] = new String[tableNames.length]; for (int i = 0; i < tableNames.length; i++) { result[i] = tableNames[i].getNameAsString(); } return result; } /** * List all of the names of userspace tables matching the given regular expression. * @param regex The regular expression to match against * @return String[] table names * @throws IOException if a remote or network exception occurs * @deprecated Use {@link Admin#listTableNames(Pattern)} instead. */ @Deprecated public String[] getTableNames(String regex) throws IOException { return getTableNames(Pattern.compile(regex)); } @Override public TableName[] listTableNames() throws IOException { return listTableNames((Pattern)null, false); } @Override public TableName[] listTableNames(Pattern pattern) throws IOException { return listTableNames(pattern, false); } @Override public TableName[] listTableNames(String regex) throws IOException { return listTableNames(Pattern.compile(regex), false); } @Override public TableName[] listTableNames(final Pattern pattern, final boolean includeSysTables) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public TableName[] call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); GetTableNamesRequest req = RequestConverter.buildGetTableNamesRequest(pattern, includeSysTables); return ProtobufUtil.getTableNameArray(master.getTableNames(controller, req) .getTableNamesList()); } }); } @Override public TableName[] listTableNames(final String regex, final boolean includeSysTables) throws IOException { return listTableNames(Pattern.compile(regex), includeSysTables); } /** * Method for getting the tableDescriptor * @param tableName as a byte [] * @return the tableDescriptor * @throws TableNotFoundException * @throws IOException if a remote or network exception occurs */ @Override public HTableDescriptor getTableDescriptor(final TableName tableName) throws TableNotFoundException, IOException { return getTableDescriptor(tableName, getConnection(), rpcCallerFactory, rpcControllerFactory, operationTimeout, rpcTimeout); } static HTableDescriptor getTableDescriptor(final TableName tableName, HConnection connection, RpcRetryingCallerFactory rpcCallerFactory, final RpcControllerFactory rpcControllerFactory, int operationTimeout, int rpcTimeout) throws TableNotFoundException, IOException { if (tableName == null) return null; HTableDescriptor htd = executeCallable(new MasterCallable(connection) { @Override public HTableDescriptor call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); GetTableDescriptorsResponse htds; GetTableDescriptorsRequest req = RequestConverter.buildGetTableDescriptorsRequest(tableName); htds = master.getTableDescriptors(controller, req); if (!htds.getTableSchemaList().isEmpty()) { return HTableDescriptor.convert(htds.getTableSchemaList().get(0)); } return null; } }, rpcCallerFactory, operationTimeout, rpcTimeout); if (htd != null) { return htd; } throw new TableNotFoundException(tableName.getNameAsString()); } public HTableDescriptor getTableDescriptor(final byte[] tableName) throws TableNotFoundException, IOException { return getTableDescriptor(TableName.valueOf(tableName)); } private long getPauseTime(int tries) { int triesCount = tries; if (triesCount >= HConstants.RETRY_BACKOFF.length) { triesCount = HConstants.RETRY_BACKOFF.length - 1; } return this.pause * HConstants.RETRY_BACKOFF[triesCount]; } /** * Creates a new table. * Synchronous operation. * * @param desc table descriptor for table * * @throws IllegalArgumentException if the table name is reserved * @throws MasterNotRunningException if master is not running * @throws TableExistsException if table already exists (If concurrent * threads, the table may have been created between test-for-existence * and attempt-at-creation). * @throws IOException if a remote or network exception occurs */ @Override public void createTable(HTableDescriptor desc) throws IOException { createTable(desc, null); } /** * 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. * * Synchronous operation. * * @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 * * @throws IllegalArgumentException if the table name is reserved * @throws MasterNotRunningException if master is not running * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent * threads, the table may have been created between test-for-existence * and attempt-at-creation). * @throws IOException if a remote or network exception occurs */ @Override public void createTable(HTableDescriptor desc, byte [] startKey, byte [] endKey, int numRegions) throws IOException { 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) { createTable(desc, new byte[][]{startKey, endKey}); return; } 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"); } createTable(desc, splitKeys); } /** * 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. Synchronous operation. * 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 * * @throws IllegalArgumentException if the table name is reserved, if the split keys * are repeated and if the split key has empty byte array. * @throws MasterNotRunningException if master is not running * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent * threads, the table may have been created between test-for-existence * and attempt-at-creation). * @throws IOException if a remote or network exception occurs */ @Override public void createTable(final HTableDescriptor desc, byte [][] splitKeys) throws IOException { Future future = createTableAsyncV2(desc, splitKeys); try { // TODO: how long should we wait? spin forever? future.get(syncWaitTimeout, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { throw new InterruptedIOException("Interrupted when waiting" + " for table to be enabled; meta scan was done"); } catch (TimeoutException e) { throw new TimeoutIOException(e); } catch (ExecutionException e) { if (e.getCause() instanceof IOException) { throw (IOException)e.getCause(); } else { throw new IOException(e.getCause()); } } } /** * Creates a new table but does not block and wait for it to come online. * Asynchronous operation. To check if the table exists, use * {@link #isTableAvailable} -- it is not safe to create an HTable * instance to this table before it is available. * Note : Avoid passing empty split key. * @param desc table descriptor for table * * @throws IllegalArgumentException Bad table name, if the split keys * are repeated and if the split key has empty byte array. * @throws MasterNotRunningException if master is not running * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent * threads, the table may have been created between test-for-existence * and attempt-at-creation). * @throws IOException if a remote or network exception occurs */ @Override public void createTableAsync(final HTableDescriptor desc, final byte [][] splitKeys) throws IOException { createTableAsyncV2(desc, splitKeys); } /** * Creates a new table but does not block and wait for it to come online. * You can use Future.get(long, TimeUnit) to wait on the operation to complete. * It may throw ExecutionException if there was an error while executing the operation * or TimeoutException in case the wait timeout was not long enough to allow the * operation to complete. * * @param desc table descriptor for table * @param splitKeys keys to check if the table has been created with all split keys * @throws IllegalArgumentException Bad table name, if the split keys * are repeated and if the split key has empty byte array. * @throws IOException if a remote or network exception occurs * @return the result of the async creation. You can use Future.get(long, TimeUnit) * to wait on the operation to complete. */ // TODO: This should be called Async but it will break binary compatibility private Future createTableAsyncV2(final HTableDescriptor desc, final byte[][] splitKeys) throws IOException { if (desc.getTableName() == null) { throw 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) { throw new IllegalArgumentException( "Empty split key must not be passed in the split keys."); } if (lastKey != null && Bytes.equals(splitKey, lastKey)) { throw new IllegalArgumentException("All split keys must be unique, " + "found duplicate: " + Bytes.toStringBinary(splitKey) + ", " + Bytes.toStringBinary(lastKey)); } lastKey = splitKey; } } CreateTableResponse response = executeCallable( new MasterCallable(getConnection()) { @Override public CreateTableResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); controller.setPriority(desc.getTableName()); CreateTableRequest request = RequestConverter.buildCreateTableRequest( desc, splitKeys, ng.getNonceGroup(), ng.newNonce()); return master.createTable(controller, request); } }); return new CreateTableFuture(this, desc, splitKeys, response); } private static class CreateTableFuture extends ProcedureFuture { private final HTableDescriptor desc; private final byte[][] splitKeys; public CreateTableFuture(final HBaseAdmin admin, final HTableDescriptor desc, final byte[][] splitKeys, final CreateTableResponse response) { super(admin, (response != null && response.hasProcId()) ? response.getProcId() : null, true); this.splitKeys = splitKeys; this.desc = desc; } @Override protected Void waitOperationResult(final long deadlineTs) throws IOException, TimeoutException { waitForTableEnabled(deadlineTs); waitForAllRegionsOnline(deadlineTs); return null; } @Override protected Void postOperationResult(final Void result, final long deadlineTs) throws IOException, TimeoutException { LOG.info("Created " + desc.getTableName()); return result; } private void waitForTableEnabled(final long deadlineTs) throws IOException, TimeoutException { waitForState(deadlineTs, new WaitForStateCallable() { @Override public boolean checkState(int tries) throws IOException { try { if (getAdmin().isTableAvailable(desc.getTableName())) { return true; } } catch (TableNotFoundException tnfe) { LOG.debug("Table "+ desc.getTableName() +" was not enabled, sleeping. tries="+ tries); } return false; } @Override public void throwInterruptedException() throws InterruptedIOException { throw new InterruptedIOException("Interrupted when waiting for table " + desc.getTableName() + " to be enabled"); } @Override public void throwTimeoutException(long elapsedTime) throws TimeoutException { throw new TimeoutException("Table " + desc.getTableName() + " not enabled after " + elapsedTime + "msec"); } }); } private void waitForAllRegionsOnline(final long deadlineTs) throws IOException, TimeoutException { final AtomicInteger actualRegCount = new AtomicInteger(0); final MetaScannerVisitor visitor = new MetaScannerVisitorBase() { @Override public boolean processRow(Result rowResult) throws IOException { RegionLocations list = MetaTableAccessor.getRegionLocations(rowResult); if (list == null) { LOG.warn("No serialized HRegionInfo in " + rowResult); return true; } HRegionLocation l = list.getRegionLocation(); if (l == null) { return true; } if (!l.getRegionInfo().getTable().equals(desc.getTableName())) { return false; } if (l.getRegionInfo().isOffline() || l.getRegionInfo().isSplit()) return true; HRegionLocation[] locations = list.getRegionLocations(); for (HRegionLocation location : locations) { if (location == null) continue; ServerName serverName = location.getServerName(); // Make sure that regions are assigned to server if (serverName != null && serverName.getHostAndPort() != null) { actualRegCount.incrementAndGet(); } } return true; } }; int tries = 0; IOException serverEx = null; int numRegs = (splitKeys == null ? 1 : splitKeys.length + 1) * desc.getRegionReplication(); while (EnvironmentEdgeManager.currentTime() < deadlineTs) { actualRegCount.set(0); MetaScanner.metaScan(getAdmin().getConnection(), visitor, desc.getTableName()); if (actualRegCount.get() == numRegs) { // all the regions are online return; } try { Thread.sleep(getAdmin().getPauseTime(tries++)); } catch (InterruptedException e) { throw new InterruptedIOException("Interrupted when opening" + " regions; " + actualRegCount.get() + " of " + numRegs + " regions processed so far"); } } throw new TimeoutException("Only " + actualRegCount.get() + " of " + numRegs + " regions are online; retries exhausted."); } } public void deleteTable(final String tableName) throws IOException { deleteTable(TableName.valueOf(tableName)); } public void deleteTable(final byte[] tableName) throws IOException { deleteTable(TableName.valueOf(tableName)); } /** * Deletes a table. * Synchronous operation. * * @param tableName name of table to delete * @throws IOException if a remote or network exception occurs */ @Override public void deleteTable(final TableName tableName) throws IOException { Future future = deleteTableAsyncV2(tableName); try { future.get(syncWaitTimeout, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { throw new InterruptedIOException("Interrupted when waiting for table to be deleted"); } catch (TimeoutException e) { throw new TimeoutIOException(e); } catch (ExecutionException e) { if (e.getCause() instanceof IOException) { throw (IOException)e.getCause(); } else { throw new IOException(e.getCause()); } } } /** * Deletes the table but does not block and wait for it be completely removed. * You can use Future.get(long, TimeUnit) to wait on the operation to complete. * It may throw ExecutionException if there was an error while executing the operation * or TimeoutException in case the wait timeout was not long enough to allow the * operation to complete. * * @param tableName name of table to delete * @throws IOException if a remote or network exception occurs * @return the result of the async delete. You can use Future.get(long, TimeUnit) * to wait on the operation to complete. */ // TODO: This should be called Async but it will break binary compatibility private Future deleteTableAsyncV2(final TableName tableName) throws IOException { DeleteTableResponse response = executeCallable( new MasterCallable(getConnection()) { @Override public DeleteTableResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); controller.setPriority(tableName); DeleteTableRequest req = RequestConverter.buildDeleteTableRequest(tableName, ng.getNonceGroup(),ng.newNonce()); return master.deleteTable(controller,req); } }); return new DeleteTableFuture(this, tableName, response); } private static class DeleteTableFuture extends ProcedureFuture { private final TableName tableName; public DeleteTableFuture(final HBaseAdmin admin, final TableName tableName, final DeleteTableResponse response) { super(admin, (response != null && response.hasProcId()) ? response.getProcId() : null); this.tableName = tableName; } @Override protected Void waitOperationResult(final long deadlineTs) throws IOException, TimeoutException { waitTableNotFound(deadlineTs); return null; } @Override protected Void postOperationResult(final Void result, final long deadlineTs) throws IOException, TimeoutException { // Delete cached information to prevent clients from using old locations getAdmin().getConnection().clearRegionCache(tableName); LOG.info("Deleted " + tableName); return result; } private void waitTableNotFound(final long deadlineTs) throws IOException, TimeoutException { waitForState(deadlineTs, new WaitForStateCallable() { @Override public boolean checkState(int tries) throws IOException { return !getAdmin().tableExists(tableName); } @Override public void throwInterruptedException() throws InterruptedIOException { throw new InterruptedIOException("Interrupted when waiting for table to be deleted"); } @Override public void throwTimeoutException(long elapsedTime) throws TimeoutException { throw new TimeoutException("Table " + tableName + " not yet deleted after " + elapsedTime + "msec"); } }); } } /** * 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(java.lang.String)} and * {@link #deleteTable(byte[])} * * @param regex The regular expression to match table names against * @return Table descriptors for tables that couldn't be deleted * @throws IOException if a remote or network exception occurs * @see #deleteTables(java.util.regex.Pattern) * @see #deleteTable(java.lang.String) */ @Override public HTableDescriptor[] deleteTables(String regex) throws IOException { return deleteTables(Pattern.compile(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(java.util.regex.Pattern) } and * {@link #deleteTable(byte[])} * * @param pattern The pattern to match table names against * @return Table descriptors for tables that couldn't be deleted * @throws IOException if a remote or network exception occurs */ @Override public HTableDescriptor[] deleteTables(Pattern pattern) throws IOException { List failed = new LinkedList(); for (HTableDescriptor table : listTables(pattern)) { try { deleteTable(table.getTableName()); } catch (IOException ex) { LOG.info("Failed to delete table " + table.getTableName(), ex); failed.add(table); } } return failed.toArray(new HTableDescriptor[failed.size()]); } /** * Truncate a table. * Synchronous operation. * * @param tableName name of table to truncate * @param preserveSplits True if the splits should be preserved * @throws IOException if a remote or network exception occurs */ @Override public void truncateTable(final TableName tableName, final boolean preserveSplits) throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { TruncateTableRequest req = RequestConverter.buildTruncateTableRequest( tableName, preserveSplits, ng.getNonceGroup(), ng.newNonce()); master.truncateTable(null, req); return null; } }); } /** * Enable a table. May timeout. Use {@link #enableTableAsync(byte[])} * and {@link #isTableEnabled(byte[])} instead. * The table has to be in disabled state for it to be enabled. * @param tableName name of the table * @throws IOException if a remote or network exception occurs * There could be couple types of IOException * TableNotFoundException means the table doesn't exist. * TableNotDisabledException means the table isn't in disabled state. * @see #isTableEnabled(byte[]) * @see #disableTable(byte[]) * @see #enableTableAsync(byte[]) */ @Override public void enableTable(final TableName tableName) throws IOException { Future future = enableTableAsyncV2(tableName); try { future.get(syncWaitTimeout, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { throw new InterruptedIOException("Interrupted when waiting for table to be disabled"); } catch (TimeoutException e) { throw new TimeoutIOException(e); } catch (ExecutionException e) { if (e.getCause() instanceof IOException) { throw (IOException)e.getCause(); } else { throw new IOException(e.getCause()); } } } public void enableTable(final byte[] tableName) throws IOException { enableTable(TableName.valueOf(tableName)); } public void enableTable(final String tableName) throws IOException { enableTable(TableName.valueOf(tableName)); } /** * Wait for the table to be enabled and available * If enabling the table exceeds the retry period, an exception is thrown. * @param tableName name of the table * @throws IOException if a remote or network exception occurs or * table is not enabled after the retries period. */ private void waitUntilTableIsEnabled(final TableName tableName) throws IOException { boolean enabled = false; long start = EnvironmentEdgeManager.currentTime(); for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) { try { enabled = isTableEnabled(tableName); } catch (TableNotFoundException tnfe) { // wait for table to be created enabled = false; } enabled = enabled && isTableAvailable(tableName); if (enabled) { break; } long sleep = getPauseTime(tries); if (LOG.isDebugEnabled()) { LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " + "enabled in " + tableName); } try { Thread.sleep(sleep); } catch (InterruptedException e) { // Do this conversion rather than let it out because do not want to // change the method signature. throw (InterruptedIOException)new InterruptedIOException("Interrupted").initCause(e); } } if (!enabled) { long msec = EnvironmentEdgeManager.currentTime() - start; throw new IOException("Table '" + tableName + "' not yet enabled, after " + msec + "ms."); } } /** * Brings a table on-line (enables it). Method returns immediately though * enable of table may take some time to complete, especially if the table * is large (All regions are opened as part of enabling process). Check * {@link #isTableEnabled(byte[])} to learn when table is fully online. If * table is taking too long to online, check server logs. * @param tableName * @throws IOException if a remote or network exception occurs * @since 0.90.0 */ @Override public void enableTableAsync(final TableName tableName) throws IOException { enableTableAsyncV2(tableName); } public void enableTableAsync(final byte[] tableName) throws IOException { enableTable(TableName.valueOf(tableName)); } public void enableTableAsync(final String tableName) throws IOException { enableTableAsync(TableName.valueOf(tableName)); } /** * Enable the table but does not block and wait for it be completely enabled. * You can use Future.get(long, TimeUnit) to wait on the operation to complete. * It may throw ExecutionException if there was an error while executing the operation * or TimeoutException in case the wait timeout was not long enough to allow the * operation to complete. * * @param tableName name of table to delete * @throws IOException if a remote or network exception occurs * @return the result of the async enable. You can use Future.get(long, TimeUnit) * to wait on the operation to complete. */ // TODO: This should be called Async but it will break binary compatibility private Future enableTableAsyncV2(final TableName tableName) throws IOException { TableName.isLegalFullyQualifiedTableName(tableName.getName()); EnableTableResponse response = executeCallable( new MasterCallable(getConnection()) { @Override public EnableTableResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); controller.setPriority(tableName); LOG.info("Started enable of " + tableName); EnableTableRequest req = RequestConverter.buildEnableTableRequest(tableName, ng.getNonceGroup(),ng.newNonce()); return master.enableTable(controller,req); } }); return new EnableTableFuture(this, tableName, response); } private static class EnableTableFuture extends ProcedureFuture { private final TableName tableName; public EnableTableFuture(final HBaseAdmin admin, final TableName tableName, final EnableTableResponse response) { super(admin, (response != null && response.hasProcId()) ? response.getProcId() : null, true); this.tableName = tableName; } @Override protected Void waitOperationResult(final long deadlineTs) throws IOException, TimeoutException { waitTableEnabled(deadlineTs); return null; } @Override protected Void postOperationResult(final Void result, final long deadlineTs) throws IOException, TimeoutException { LOG.info("Enabled " + tableName); return result; } private void waitTableEnabled(final long deadlineTs) throws IOException, TimeoutException { waitForState(deadlineTs, new WaitForStateCallable() { @Override public boolean checkState(int tries) throws IOException { boolean enabled; try { enabled = getAdmin().isTableEnabled(tableName); } catch (TableNotFoundException tnfe) { return false; } return enabled && getAdmin().isTableAvailable(tableName); } @Override public void throwInterruptedException() throws InterruptedIOException { throw new InterruptedIOException("Interrupted when waiting for table to be enabled"); } @Override public void throwTimeoutException(long elapsedTime) throws TimeoutException { throw new TimeoutException("Table " + tableName + " not yet enabled after " + elapsedTime + "msec"); } }); } } /** * Enable 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(java.lang.String)} and * {@link #enableTable(byte[])} * * @param regex The regular expression to match table names against * @throws IOException if a remote or network exception occurs * @see #enableTables(java.util.regex.Pattern) * @see #enableTable(java.lang.String) */ @Override public HTableDescriptor[] enableTables(String regex) throws IOException { return enableTables(Pattern.compile(regex)); } /** * Enable 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(java.util.regex.Pattern) } and * {@link #enableTable(byte[])} * * @param pattern The pattern to match table names against * @throws IOException if a remote or network exception occurs */ @Override public HTableDescriptor[] enableTables(Pattern pattern) throws IOException { List failed = new LinkedList(); for (HTableDescriptor table : listTables(pattern)) { if (isTableDisabled(table.getTableName())) { try { enableTable(table.getTableName()); } catch (IOException ex) { LOG.info("Failed to enable table " + table.getTableName(), ex); failed.add(table); } } } return failed.toArray(new HTableDescriptor[failed.size()]); } /** * Starts the disable of a table. If it is being served, the master * will tell the servers to stop serving it. This method returns immediately. * The disable of a table can take some time if the table is large (all * regions are closed as part of table disable operation). * Call {@link #isTableDisabled(byte[])} to check for when disable completes. * If table is taking too long to online, check server logs. * @param tableName name of table * @throws IOException if a remote or network exception occurs * @see #isTableDisabled(byte[]) * @see #isTableEnabled(byte[]) * @since 0.90.0 */ @Override public void disableTableAsync(final TableName tableName) throws IOException { disableTableAsyncV2(tableName); } public void disableTableAsync(final byte[] tableName) throws IOException { disableTableAsync(TableName.valueOf(tableName)); } public void disableTableAsync(final String tableName) throws IOException { disableTableAsync(TableName.valueOf(tableName)); } /** * Disable table and wait on completion. May timeout eventually. Use * {@link #disableTableAsync(byte[])} and {@link #isTableDisabled(String)} * instead. * The table has to be in enabled state for it to be disabled. * @param tableName * @throws IOException if a remote or network exception occurs * There could be couple types of IOException * TableNotFoundException means the table doesn't exist. * TableNotEnabledException means the table isn't in enabled state. */ @Override public void disableTable(final TableName tableName) throws IOException { Future future = disableTableAsyncV2(tableName); try { future.get(syncWaitTimeout, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { throw new InterruptedIOException("Interrupted when waiting for table to be disabled"); } catch (TimeoutException e) { throw new TimeoutIOException(e); } catch (ExecutionException e) { if (e.getCause() instanceof IOException) { throw (IOException)e.getCause(); } else { throw new IOException(e.getCause()); } } } public void disableTable(final byte[] tableName) throws IOException { disableTable(TableName.valueOf(tableName)); } public void disableTable(final String tableName) throws IOException { disableTable(TableName.valueOf(tableName)); } /** * Disable the table but does not block and wait for it be completely disabled. * You can use Future.get(long, TimeUnit) to wait on the operation to complete. * It may throw ExecutionException if there was an error while executing the operation * or TimeoutException in case the wait timeout was not long enough to allow the * operation to complete. * * @param tableName name of table to delete * @throws IOException if a remote or network exception occurs * @return the result of the async disable. You can use Future.get(long, TimeUnit) * to wait on the operation to complete. */ // TODO: This should be called Async but it will break binary compatibility private Future disableTableAsyncV2(final TableName tableName) throws IOException { TableName.isLegalFullyQualifiedTableName(tableName.getName()); DisableTableResponse response = executeCallable( new MasterCallable(getConnection()) { @Override public DisableTableResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); controller.setPriority(tableName); LOG.info("Started disable of " + tableName); DisableTableRequest req = RequestConverter.buildDisableTableRequest( tableName, ng.getNonceGroup(), ng.newNonce()); return master.disableTable(controller, req); } }); return new DisableTableFuture(this, tableName, response); } private static class DisableTableFuture extends ProcedureFuture { private final TableName tableName; public DisableTableFuture(final HBaseAdmin admin, final TableName tableName, final DisableTableResponse response) { super(admin, (response != null && response.hasProcId()) ? response.getProcId() : null); this.tableName = tableName; } @Override protected Void waitOperationResult(final long deadlineTs) throws IOException, TimeoutException { waitTableDisabled(deadlineTs); return null; } @Override protected Void postOperationResult(final Void result, final long deadlineTs) throws IOException, TimeoutException { LOG.info("Disabled " + tableName); return result; } private void waitTableDisabled(final long deadlineTs) throws IOException, TimeoutException { waitForState(deadlineTs, new WaitForStateCallable() { @Override public boolean checkState(int tries) throws IOException { return getAdmin().isTableDisabled(tableName); } @Override public void throwInterruptedException() throws InterruptedIOException { throw new InterruptedIOException("Interrupted when waiting for table to be disabled"); } @Override public void throwTimeoutException(long elapsedTime) throws TimeoutException { throw new TimeoutException("Table " + tableName + " not yet disabled after " + elapsedTime + "msec"); } }); } } /** * Disable 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(java.lang.String)} and * {@link #disableTable(byte[])} * * @param regex The regular expression to match table names against * @return Table descriptors for tables that couldn't be disabled * @throws IOException if a remote or network exception occurs * @see #disableTables(java.util.regex.Pattern) * @see #disableTable(java.lang.String) */ @Override public HTableDescriptor[] disableTables(String regex) throws IOException { return disableTables(Pattern.compile(regex)); } /** * Disable 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(java.util.regex.Pattern) } and * {@link #disableTable(byte[])} * * @param pattern The pattern to match table names against * @return Table descriptors for tables that couldn't be disabled * @throws IOException if a remote or network exception occurs */ @Override public HTableDescriptor[] disableTables(Pattern pattern) throws IOException { List failed = new LinkedList(); for (HTableDescriptor table : listTables(pattern)) { if (isTableEnabled(table.getTableName())) { try { disableTable(table.getTableName()); } catch (IOException ex) { LOG.info("Failed to disable table " + table.getTableName(), ex); failed.add(table); } } } return failed.toArray(new HTableDescriptor[failed.size()]); } /* * Checks whether table exists. If not, throws TableNotFoundException * @param tableName */ private void checkTableExistence(TableName tableName) throws IOException { if (!tableExists(tableName)) { throw new TableNotFoundException(tableName); } } /** * @param tableName name of table to check * @return true if table is on-line * @throws IOException if a remote or network exception occurs */ @Override public boolean isTableEnabled(TableName tableName) throws IOException { checkTableExistence(tableName); return connection.isTableEnabled(tableName); } public boolean isTableEnabled(byte[] tableName) throws IOException { return isTableEnabled(TableName.valueOf(tableName)); } public boolean isTableEnabled(String tableName) throws IOException { return isTableEnabled(TableName.valueOf(tableName)); } /** * @param tableName name of table to check * @return true if table is off-line * @throws IOException if a remote or network exception occurs */ @Override public boolean isTableDisabled(TableName tableName) throws IOException { checkTableExistence(tableName); return connection.isTableDisabled(tableName); } public boolean isTableDisabled(byte[] tableName) throws IOException { return isTableDisabled(TableName.valueOf(tableName)); } public boolean isTableDisabled(String tableName) throws IOException { return isTableDisabled(TableName.valueOf(tableName)); } /** * @param tableName name of table to check * @return true if all regions of the table are available * @throws IOException if a remote or network exception occurs */ @Override public boolean isTableAvailable(TableName tableName) throws IOException { return connection.isTableAvailable(tableName); } public boolean isTableAvailable(byte[] tableName) throws IOException { return isTableAvailable(TableName.valueOf(tableName)); } public boolean isTableAvailable(String tableName) throws IOException { return isTableAvailable(TableName.valueOf(tableName)); } /** * Use this api to check if the table has been created with the specified number of * splitkeys which was used while creating the given table. * Note : If this api is used after a table's region gets splitted, the api may return * false. * @param tableName * name of table to check * @param splitKeys * keys to check if the table has been created with all split keys * @throws IOException * if a remote or network excpetion occurs */ @Override public boolean isTableAvailable(TableName tableName, byte[][] splitKeys) throws IOException { return connection.isTableAvailable(tableName, splitKeys); } public boolean isTableAvailable(byte[] tableName, byte[][] splitKeys) throws IOException { return isTableAvailable(TableName.valueOf(tableName), splitKeys); } public boolean isTableAvailable(String tableName, byte[][] splitKeys) throws IOException { return isTableAvailable(TableName.valueOf(tableName), splitKeys); } /** * Get the status of alter command - indicates how many regions have received * the updated schema Asynchronous operation. * * @param tableName TableName instance * @return Pair indicating the number of regions updated Pair.getFirst() is the * regions that are yet to be updated Pair.getSecond() is the total number * of regions of the table * @throws IOException * if a remote or network exception occurs */ @Override public Pair getAlterStatus(final TableName tableName) throws IOException { return executeCallable(new MasterCallable>(getConnection()) { @Override public Pair call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); controller.setPriority(tableName); GetSchemaAlterStatusRequest req = RequestConverter .buildGetSchemaAlterStatusRequest(tableName); GetSchemaAlterStatusResponse ret = master.getSchemaAlterStatus(controller, req); Pair pair = new Pair(Integer.valueOf(ret .getYetToUpdateRegions()), Integer.valueOf(ret.getTotalRegions())); return pair; } }); } /** * Get the status of alter command - indicates how many regions have received * the updated schema Asynchronous operation. * * @param tableName * name of the table to get the status of * @return Pair indicating the number of regions updated Pair.getFirst() is the * regions that are yet to be updated Pair.getSecond() is the total number * of regions of the table * @throws IOException * if a remote or network exception occurs */ @Override public Pair getAlterStatus(final byte[] tableName) throws IOException { return getAlterStatus(TableName.valueOf(tableName)); } /** * Add a column to an existing table. * Asynchronous operation. * * @param tableName name of the table to add column to * @param column column descriptor of column to be added * @throws IOException if a remote or network exception occurs */ public void addColumn(final byte[] tableName, HColumnDescriptor column) throws IOException { addColumn(TableName.valueOf(tableName), column); } /** * Add a column to an existing table. * Asynchronous operation. * * @param tableName name of the table to add column to * @param column column descriptor of column to be added * @throws IOException if a remote or network exception occurs */ public void addColumn(final String tableName, HColumnDescriptor column) throws IOException { addColumn(TableName.valueOf(tableName), column); } /** * Add a column to an existing table. * Asynchronous operation. * * @param tableName name of the table to add column to * @param column column descriptor of column to be added * @throws IOException if a remote or network exception occurs */ @Override public void addColumn(final TableName tableName, final HColumnDescriptor column) throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); controller.setPriority(tableName); AddColumnRequest req = RequestConverter.buildAddColumnRequest( tableName, column, ng.getNonceGroup(), ng.newNonce()); master.addColumn(controller,req); return null; } }); } /** * Delete a column from a table. * Asynchronous operation. * * @param tableName name of table * @param columnName name of column to be deleted * @throws IOException if a remote or network exception occurs */ public void deleteColumn(final byte[] tableName, final String columnName) throws IOException { deleteColumn(TableName.valueOf(tableName), Bytes.toBytes(columnName)); } /** * Delete a column from a table. * Asynchronous operation. * * @param tableName name of table * @param columnName name of column to be deleted * @throws IOException if a remote or network exception occurs */ public void deleteColumn(final String tableName, final String columnName) throws IOException { deleteColumn(TableName.valueOf(tableName), Bytes.toBytes(columnName)); } /** * Delete a column from a table. * Asynchronous operation. * * @param tableName name of table * @param columnName name of column to be deleted * @throws IOException if a remote or network exception occurs */ @Override public void deleteColumn(final TableName tableName, final byte [] columnName) throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); controller.setPriority(tableName); DeleteColumnRequest req = RequestConverter.buildDeleteColumnRequest( tableName, columnName, ng.getNonceGroup(), ng.newNonce()); master.deleteColumn(controller, req); return null; } }); } /** * Modify an existing column family on a table. * Asynchronous operation. * * @param tableName name of table * @param descriptor new column descriptor to use * @throws IOException if a remote or network exception occurs */ public void modifyColumn(final String tableName, HColumnDescriptor descriptor) throws IOException { modifyColumn(TableName.valueOf(tableName), descriptor); } /** * Modify an existing column family on a table. * Asynchronous operation. * * @param tableName name of table * @param descriptor new column descriptor to use * @throws IOException if a remote or network exception occurs */ public void modifyColumn(final byte[] tableName, HColumnDescriptor descriptor) throws IOException { modifyColumn(TableName.valueOf(tableName), descriptor); } /** * Modify an existing column family on a table. * Asynchronous operation. * * @param tableName name of table * @param descriptor new column descriptor to use * @throws IOException if a remote or network exception occurs */ @Override public void modifyColumn(final TableName tableName, final HColumnDescriptor descriptor) throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); controller.setPriority(tableName); ModifyColumnRequest req = RequestConverter.buildModifyColumnRequest( tableName, descriptor, ng.getNonceGroup(), ng.newNonce()); master.modifyColumn(controller, req); return null; } }); } /** * Close a region. For expert-admins. Runs close on the regionserver. The * master will not be informed of the close. * @param regionname region name to close * @param serverName If supplied, we'll use this location rather than * the one currently in hbase:meta * @throws IOException if a remote or network exception occurs */ @Override public void closeRegion(final String regionname, final String serverName) throws IOException { closeRegion(Bytes.toBytes(regionname), serverName); } /** * Close a region. For expert-admins Runs close on the regionserver. The * master will not be informed of the close. * @param regionname region name to close * @param serverName The servername of the regionserver. If passed null we * will use servername found in the hbase:meta table. A server name * is made of host, port and startcode. Here is an example: * host187.example.com,60020,1289493121758 * @throws IOException if a remote or network exception occurs */ @Override public void closeRegion(final byte [] regionname, final String serverName) throws IOException { if (serverName != null) { Pair pair = MetaTableAccessor.getRegion(connection, regionname); if (pair == null || pair.getFirst() == null) { throw new UnknownRegionException(Bytes.toStringBinary(regionname)); } else { closeRegion(ServerName.valueOf(serverName), pair.getFirst()); } } else { Pair pair = MetaTableAccessor.getRegion(connection, regionname); if (pair == null) { throw new UnknownRegionException(Bytes.toStringBinary(regionname)); } else if (pair.getSecond() == null) { throw new NoServerForRegionException(Bytes.toStringBinary(regionname)); } else { closeRegion(pair.getSecond(), pair.getFirst()); } } } /** * For expert-admins. Runs close on the regionserver. Closes a region based on * the encoded region name. The region server name is mandatory. If the * servername is provided then based on the online regions in the specified * regionserver the specified region will be closed. The master will not be * informed of the close. Note that the regionname is the encoded regionname. * * @param encodedRegionName * The encoded region name; i.e. the hash that makes up the region * name suffix: e.g. if regionname is * TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396. * , then the encoded region name is: * 527db22f95c8a9e0116f0cc13c680396. * @param serverName * The servername of the regionserver. A server name is made of host, * port and startcode. This is mandatory. Here is an example: * host187.example.com,60020,1289493121758 * @return true if the region was closed, false if not. * @throws IOException * if a remote or network exception occurs */ @Override public boolean closeRegionWithEncodedRegionName(final String encodedRegionName, final String serverName) throws IOException { if (null == serverName || ("").equals(serverName.trim())) { throw new IllegalArgumentException( "The servername cannot be null or empty."); } ServerName sn = ServerName.valueOf(serverName); AdminService.BlockingInterface admin = this.connection.getAdmin(sn); // Close the region without updating zk state. CloseRegionRequest request = RequestConverter.buildCloseRegionRequest(sn, encodedRegionName, false); try { HBaseRpcController controller = rpcControllerFactory.newController(); // TODO: this does not do retries, it should. Set priority and timeout in controller CloseRegionResponse response = admin.closeRegion(controller, request); boolean isRegionClosed = response.getClosed(); if (false == isRegionClosed) { LOG.error("Not able to close the region " + encodedRegionName + "."); } return isRegionClosed; } catch (ServiceException se) { throw ProtobufUtil.getRemoteException(se); } } /** * Close a region. For expert-admins Runs close on the regionserver. The * master will not be informed of the close. * @param sn * @param hri * @throws IOException if a remote or network exception occurs */ @Override public void closeRegion(final ServerName sn, final HRegionInfo hri) throws IOException { AdminService.BlockingInterface admin = this.connection.getAdmin(sn); HBaseRpcController controller = rpcControllerFactory.newController(); // Close the region without updating zk state. ProtobufUtil.closeRegion(controller, admin, sn, hri.getRegionName(), false); } /** * Get all the online regions on a region server. */ @Override public List getOnlineRegions(final ServerName sn) throws IOException { AdminService.BlockingInterface admin = this.connection.getAdmin(sn); HBaseRpcController controller = rpcControllerFactory.newController(); return ProtobufUtil.getOnlineRegions(controller, admin); } /** * {@inheritDoc} */ @Override public void flush(final TableName tableName) throws IOException { checkTableExists(tableName); if (isTableDisabled(tableName)) { LOG.info("Table is disabled: " + tableName.getNameAsString()); return; } execProcedure("flush-table-proc", tableName.getNameAsString(), new HashMap()); } /** * {@inheritDoc} */ @Override public void flushRegion(final byte[] regionName) throws IOException { Pair regionServerPair = getRegion(regionName); if (regionServerPair == null) { throw new IllegalArgumentException("Unknown regionname: " + Bytes.toStringBinary(regionName)); } if (regionServerPair.getSecond() == null) { throw new NoServerForRegionException(Bytes.toStringBinary(regionName)); } flush(regionServerPair.getSecond(), regionServerPair.getFirst()); } /** * @deprecated Use {@link #flush(org.apache.hadoop.hbase.TableName)} or {@link #flushRegion * (byte[])} instead. */ @Deprecated public void flush(final String tableNameOrRegionName) throws IOException, InterruptedException { flush(Bytes.toBytes(tableNameOrRegionName)); } /** * @deprecated Use {@link #flush(org.apache.hadoop.hbase.TableName)} or {@link #flushRegion * (byte[])} instead. */ @Deprecated public void flush(final byte[] tableNameOrRegionName) throws IOException, InterruptedException { try { flushRegion(tableNameOrRegionName); } catch (IllegalArgumentException e) { // Unknown region. Try table. flush(TableName.valueOf(tableNameOrRegionName)); } } private void flush(final ServerName sn, final HRegionInfo hri) throws IOException { HBaseRpcController controller = rpcControllerFactory.newController(); AdminService.BlockingInterface admin = this.connection.getAdmin(sn); FlushRegionRequest request = RequestConverter.buildFlushRegionRequest(hri.getRegionName()); try { admin.flushRegion(controller, request); } catch (ServiceException se) { throw ProtobufUtil.getRemoteException(se); } } /** * {@inheritDoc} */ @Override public void compact(final TableName tableName) throws IOException { compact(tableName, null, false); } /** * {@inheritDoc} */ @Override public void compactRegion(final byte[] regionName) throws IOException { compactRegion(regionName, null, false); } /** * @deprecated Use {@link #compact(org.apache.hadoop.hbase.TableName)} or {@link #compactRegion * (byte[])} instead. */ @Deprecated public void compact(final String tableNameOrRegionName) throws IOException { compact(Bytes.toBytes(tableNameOrRegionName)); } /** * @deprecated Use {@link #compact(org.apache.hadoop.hbase.TableName)} or {@link #compactRegion * (byte[])} instead. */ @Deprecated public void compact(final byte[] tableNameOrRegionName) throws IOException { try { compactRegion(tableNameOrRegionName, null, false); } catch (IllegalArgumentException e) { compact(TableName.valueOf(tableNameOrRegionName), null, false); } } /** * {@inheritDoc} */ @Override public void compact(final TableName tableName, final byte[] columnFamily) throws IOException { compact(tableName, columnFamily, false); } /** * {@inheritDoc} */ @Override public void compactRegion(final byte[] regionName, final byte[] columnFamily) throws IOException { compactRegion(regionName, columnFamily, false); } /** * @deprecated Use {@link #compact(org.apache.hadoop.hbase.TableName)} or {@link #compactRegion * (byte[], byte[])} instead. */ @Deprecated public void compact(String tableOrRegionName, String columnFamily) throws IOException { compact(Bytes.toBytes(tableOrRegionName), Bytes.toBytes(columnFamily)); } /** * @deprecated Use {@link #compact(org.apache.hadoop.hbase.TableName)} or {@link #compactRegion * (byte[], byte[])} instead. */ @Deprecated public void compact(final byte[] tableNameOrRegionName, final byte[] columnFamily) throws IOException { try { compactRegion(tableNameOrRegionName, columnFamily, false); } catch (IllegalArgumentException e) { // Bad region, try table compact(TableName.valueOf(tableNameOrRegionName), columnFamily, false); } } /** * {@inheritDoc} */ @Override public Map compactionSwitch(boolean switchState, List serverNamesList) throws IOException { List serverList = new ArrayList<>(); if (serverNamesList.isEmpty()) { ClusterStatus status = getClusterStatus(); serverList.addAll(status.getServers()); } else { for (String regionServerName : serverNamesList) { ServerName serverName = null; try { serverName = ServerName.valueOf(regionServerName); } catch (Exception e) { throw new IllegalArgumentException(String.format("Invalid format for regionserver:%s ", regionServerName)); } if (serverName == null) { throw new IllegalArgumentException(String.format("Invalid regionserver:%s ", regionServerName)); } serverList.add(serverName); } } HBaseRpcController controller = rpcControllerFactory.newController(); Map res = new HashMap<>(serverList.size()); for (ServerName serverName : serverList) { AdminService.BlockingInterface admin = this.connection.getAdmin(serverName); try { CompactionSwitchRequest request = CompactionSwitchRequest.newBuilder().setEnabled(switchState).build(); CompactionSwitchResponse compactionSwitchResponse = admin.compactionSwitch(controller, request); boolean prev_state = compactionSwitchResponse.getPrevState(); res.put(serverName, prev_state); } catch (ServiceException se) { throw ProtobufUtil.getRemoteException(se); } } return res; } /** * * {@inheritDoc} */ @Override public void compactRegionServer(final ServerName sn, boolean major) throws IOException, InterruptedException { for (HRegionInfo region : getOnlineRegions(sn)) { compact(sn, region, major, null); } } /** * {@inheritDoc} */ @Override public void majorCompact(final TableName tableName) throws IOException { compact(tableName, null, true); } /** * {@inheritDoc} */ @Override public void majorCompactRegion(final byte[] regionName) throws IOException { compactRegion(regionName, null, true); } /** * @deprecated Use {@link #majorCompact(org.apache.hadoop.hbase.TableName)} or {@link * #majorCompactRegion(byte[])} instead. */ @Deprecated public void majorCompact(final String tableNameOrRegionName) throws IOException { majorCompact(Bytes.toBytes(tableNameOrRegionName)); } /** * @deprecated Use {@link #majorCompact(org.apache.hadoop.hbase.TableName)} or {@link * #majorCompactRegion(byte[])} instead. */ @Deprecated public void majorCompact(final byte[] tableNameOrRegionName) throws IOException { try { compactRegion(tableNameOrRegionName, null, true); } catch (IllegalArgumentException e) { // Invalid region, try table compact(TableName.valueOf(tableNameOrRegionName), null, true); } } /** * {@inheritDoc} */ @Override public void majorCompact(final TableName tableName, final byte[] columnFamily) throws IOException { compact(tableName, columnFamily, true); } /** * {@inheritDoc} */ @Override public void majorCompactRegion(final byte[] regionName, final byte[] columnFamily) throws IOException { compactRegion(regionName, columnFamily, true); } /** * @deprecated Use {@link #majorCompact(org.apache.hadoop.hbase.TableName, * byte[])} or {@link #majorCompactRegion(byte[], byte[])} instead. */ @Deprecated public void majorCompact(final String tableNameOrRegionName, final String columnFamily) throws IOException { majorCompact(Bytes.toBytes(tableNameOrRegionName), Bytes.toBytes(columnFamily)); } /** * @deprecated Use {@link #majorCompact(org.apache.hadoop.hbase.TableName, * byte[])} or {@link #majorCompactRegion(byte[], byte[])} instead. */ @Deprecated public void majorCompact(final byte[] tableNameOrRegionName, final byte[] columnFamily) throws IOException { try { compactRegion(tableNameOrRegionName, columnFamily, true); } catch (IllegalArgumentException e) { // Invalid region, try table compact(TableName.valueOf(tableNameOrRegionName), columnFamily, true); } } /** * Compact a table. * Asynchronous operation. * * @param tableName table or region to compact * @param columnFamily column family within a table or region * @param major True if we are to do a major compaction. * @throws IOException if a remote or network exception occurs * @throws InterruptedException */ private void compact(final TableName tableName, final byte[] columnFamily,final boolean major) throws IOException { ZooKeeperWatcher zookeeper = null; try { checkTableExists(tableName); zookeeper = new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(), new ThrowableAbortable()); List> pairs = MetaTableAccessor.getTableRegionsAndLocations(zookeeper, connection, tableName); for (Pair pair: pairs) { if (pair.getFirst().isOffline()) continue; if (pair.getSecond() == null) continue; try { compact(pair.getSecond(), pair.getFirst(), major, columnFamily); } catch (NotServingRegionException e) { if (LOG.isDebugEnabled()) { LOG.debug("Trying to" + (major ? " major" : "") + " compact " + pair.getFirst() + ": " + StringUtils.stringifyException(e)); } } } } finally { if (zookeeper != null) { zookeeper.close(); } } } /** * Compact an individual region. * Asynchronous operation. * * @param regionName region to compact * @param columnFamily column family within a table or region * @param major True if we are to do a major compaction. * @throws IOException if a remote or network exception occurs * @throws InterruptedException */ private void compactRegion(final byte[] regionName, final byte[] columnFamily,final boolean major) throws IOException { Pair regionServerPair = getRegion(regionName); if (regionServerPair == null) { throw new IllegalArgumentException("Invalid region: " + Bytes.toStringBinary(regionName)); } if (regionServerPair.getSecond() == null) { throw new NoServerForRegionException(Bytes.toStringBinary(regionName)); } compact(regionServerPair.getSecond(), regionServerPair.getFirst(), major, columnFamily); } private void compact(final ServerName sn, final HRegionInfo hri, final boolean major, final byte [] family) throws IOException { HBaseRpcController controller = rpcControllerFactory.newController(); AdminService.BlockingInterface admin = this.connection.getAdmin(sn); CompactRegionRequest request = RequestConverter.buildCompactRegionRequest(hri.getRegionName(), major, family); try { // TODO: this does not do retries, it should. Set priority and timeout in controller admin.compactRegion(controller, request); } catch (ServiceException se) { throw ProtobufUtil.getRemoteException(se); } } /** * Move the region r to dest. * @param encodedRegionName The encoded region name; i.e. the hash that makes * up the region name suffix: e.g. if regionname is * TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396., * then the encoded region name is: 527db22f95c8a9e0116f0cc13c680396. * @param destServerName The servername of the destination regionserver. If * passed the empty byte array we'll assign to a random server. A server name * is made of host, port and startcode. Here is an example: * host187.example.com,60020,1289493121758 * @throws UnknownRegionException Thrown if we can't find a region named * encodedRegionName */ @Override public void move(final byte [] encodedRegionName, final byte [] destServerName) throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); // Hard to know the table name, at least check if meta if (isMetaRegion(encodedRegionName)) { controller.setPriority(TableName.META_TABLE_NAME); } try { MoveRegionRequest request = RequestConverter.buildMoveRegionRequest(encodedRegionName, destServerName); master.moveRegion(controller, request); } catch (DeserializationException de) { LOG.error("Could not parse destination server name: " + de); throw new ServiceException(new DoNotRetryIOException(de)); } return null; } }); } private boolean isMetaRegion(final byte[] regionName) { return Bytes.equals(regionName, HRegionInfo.FIRST_META_REGIONINFO.getRegionName()) || Bytes.equals(regionName, HRegionInfo.FIRST_META_REGIONINFO.getEncodedNameAsBytes()); } /** * @param regionName * Region name to assign. * @throws MasterNotRunningException * @throws ZooKeeperConnectionException * @throws IOException if a remote or network exception occurs */ @Override public void assign(final byte[] regionName) throws MasterNotRunningException, ZooKeeperConnectionException, IOException { final byte[] toBeAssigned = getRegionName(regionName); executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); // Hard to know the table name, at least check if meta if (isMetaRegion(regionName)) { controller.setPriority(TableName.META_TABLE_NAME); } AssignRegionRequest request = RequestConverter.buildAssignRegionRequest(toBeAssigned); master.assignRegion(controller,request); return null; } }); } /** * Unassign a region from current hosting regionserver. Region will then be * assigned to a regionserver chosen at random. Region could be reassigned * back to the same server. Use {@link #move(byte[], byte[])} if you want * to control the region movement. * @param regionName Region to unassign. Will clear any existing RegionPlan * if one found. * @param force If true, force unassign (Will remove region from * regions-in-transition too if present. If results in double assignment * use hbck -fix to resolve. To be used by experts). * @throws MasterNotRunningException * @throws ZooKeeperConnectionException * @throws IOException if a remote or network exception occurs */ @Override public void unassign(final byte [] regionName, final boolean force) throws MasterNotRunningException, ZooKeeperConnectionException, IOException { final byte[] toBeUnassigned = getRegionName(regionName); executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); // Hard to know the table name, at least check if meta if (isMetaRegion(regionName)) { controller.setPriority(TableName.META_TABLE_NAME); } UnassignRegionRequest request = RequestConverter.buildUnassignRegionRequest(toBeUnassigned, force); master.unassignRegion(controller, request); return null; } }); } /** * Offline specified region from master's in-memory state. It will not attempt to reassign the * region as in unassign. This API can be used when a region not served by any region server and * still online as per Master's in memory state. If this API is incorrectly used on active region * then master will loose track of that region. * * This is a special method that should be used by experts or hbck. * * @param regionName * Region to offline. * @throws IOException if a remote or network exception occurs */ @Override public void offline(final byte [] regionName) throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); // Hard to know the table name, at least check if meta if (isMetaRegion(regionName)) { controller.setPriority(TableName.META_TABLE_NAME); } master.offlineRegion(controller, RequestConverter.buildOfflineRegionRequest(regionName)); return null; } }); } /** * Turn the load balancer on or off. * @param on If true, enable balancer. If false, disable balancer. * @param synchronous If true, it waits until current balance() call, if outstanding, to return. * @return Previous balancer value */ @Override public boolean setBalancerRunning(final boolean on, final boolean synchronous) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); SetBalancerRunningRequest req = RequestConverter.buildSetBalancerRunningRequest(on, synchronous); return master.setBalancerRunning(controller, req).getPrevBalanceValue(); } }); } /** * Invoke the balancer. Will run the balancer and if regions to move, it will * go ahead and do the reassignments. Can NOT run for various reasons. Check * logs. * @return True if balancer ran, false otherwise. */ @Override public boolean balancer() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.balance(controller, RequestConverter.buildBalanceRequest(false)).getBalancerRan(); } }); } @Override public boolean balancer(final boolean force) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.balance(controller, RequestConverter.buildBalanceRequest(force)).getBalancerRan(); } }); } /** * Query the state of the balancer from the Master. It's not a guarantee that the balancer is * actually running this very moment, but that it will run. * * @return True if the balancer is enabled, false otherwise. */ @Override public boolean isBalancerEnabled() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.isBalancerEnabled(controller, RequestConverter.buildIsBalancerEnabledRequest()).getEnabled(); } }); } /** * Invoke region normalizer. Can NOT run for various reasons. Check logs. * * @return True if region normalizer ran, false otherwise. */ @Override public boolean normalize() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.normalize(controller, RequestConverter.buildNormalizeRequest()).getNormalizerRan(); } }); } /** * Query the current state of the region normalizer * * @return true if region normalizer is enabled, false otherwise. */ @Override public boolean isNormalizerEnabled() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.isNormalizerEnabled(controller, RequestConverter.buildIsNormalizerEnabledRequest()).getEnabled(); } }); } /** * Turn region normalizer on or off. * * @return Previous normalizer value */ @Override public boolean setNormalizerRunning(final boolean on) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); SetNormalizerRunningRequest req = RequestConverter.buildSetNormalizerRunningRequest(on); return master.setNormalizerRunning(controller, req).getPrevNormalizerValue(); } }); } /** * Enable/Disable the catalog janitor * @param enable if true enables the catalog janitor * @return the previous state * @throws MasterNotRunningException */ @Override public boolean enableCatalogJanitor(final boolean enable) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.enableCatalogJanitor(controller, RequestConverter.buildEnableCatalogJanitorRequest(enable)).getPrevValue(); } }); } /** * Ask for a scan of the catalog table * @return the number of entries cleaned * @throws MasterNotRunningException */ @Override public int runCatalogScan() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Integer call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.runCatalogScan(controller, RequestConverter.buildCatalogScanRequest()).getScanResult(); } }); } /** * Query on the catalog janitor state (Enabled/Disabled?) * @throws org.apache.hadoop.hbase.MasterNotRunningException */ @Override public boolean isCatalogJanitorEnabled() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.isCatalogJanitorEnabled(controller, RequestConverter.buildIsCatalogJanitorEnabledRequest()).getValue(); } }); } @Override public boolean setCleanerChoreRunning(final boolean on) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { return master.setCleanerChoreRunning(null, RequestConverter.buildSetCleanerChoreRunningRequest(on)).getPrevValue(); } }); } @Override public boolean runCleanerChore() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { return master.runCleanerChore(null, RequestConverter.buildCleanerChoreRequest()) .getCleanerChoreRan(); } }); } @Override public boolean isCleanerChoreEnabled() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { return master.isCleanerChoreEnabled(null, RequestConverter.buildIsCleanerChoreEnabledRequest()).getValue(); } }); } private boolean isEncodedRegionName(byte[] regionName) throws IOException { try { HRegionInfo.parseRegionName(regionName); return false; } catch (IOException e) { if (StringUtils.stringifyException(e) .contains(HRegionInfo.INVALID_REGION_NAME_FORMAT_MESSAGE)) { return true; } throw e; } } /** * Merge two regions. Asynchronous operation. * @param nameOfRegionA encoded or full name of region a * @param nameOfRegionB encoded or full name of region b * @param forcible true if do a compulsory merge, otherwise we will only merge * two adjacent regions * @throws IOException if a remote or network exception occurs */ @Override public void mergeRegions(final byte[] nameOfRegionA, final byte[] nameOfRegionB, final boolean forcible) throws IOException { final byte[] encodedNameOfRegionA = isEncodedRegionName(nameOfRegionA) ? nameOfRegionA : Bytes.toBytes(HRegionInfo.encodeRegionName(nameOfRegionA)); final byte[] encodedNameOfRegionB = isEncodedRegionName(nameOfRegionB) ? nameOfRegionB : Bytes.toBytes(HRegionInfo.encodeRegionName(nameOfRegionB)); Pair pair = getRegion(nameOfRegionA); if (pair != null && pair.getFirst().getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID) throw new IllegalArgumentException("Can't invoke merge on non-default regions directly"); pair = getRegion(nameOfRegionB); if (pair != null && pair.getFirst().getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID) throw new IllegalArgumentException("Can't invoke merge on non-default regions directly"); executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); try { DispatchMergingRegionsRequest request = RequestConverter .buildDispatchMergingRegionsRequest(encodedNameOfRegionA, encodedNameOfRegionB, forcible); master.dispatchMergingRegions(controller, request); } catch (DeserializationException de) { LOG.error("Could not parse destination server name: " + de); } return null; } }); } /** * {@inheritDoc} */ @Override public void split(final TableName tableName) throws IOException { split(tableName, null); } /** * {@inheritDoc} */ @Override public void splitRegion(final byte[] regionName) throws IOException { splitRegion(regionName, null); } /** * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName)} or {@link #splitRegion * (byte[])} instead. */ @Deprecated public void split(final String tableNameOrRegionName) throws IOException, InterruptedException { split(Bytes.toBytes(tableNameOrRegionName)); } /** * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName)} or {@link #splitRegion * (byte[])} instead. */ @Deprecated public void split(final byte[] tableNameOrRegionName) throws IOException, InterruptedException { split(tableNameOrRegionName, null); } /** * {@inheritDoc} */ @Override public void split(final TableName tableName, final byte [] splitPoint) throws IOException { ZooKeeperWatcher zookeeper = null; try { checkTableExists(tableName); zookeeper = new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(), new ThrowableAbortable()); List> pairs = MetaTableAccessor.getTableRegionsAndLocations(zookeeper, connection, tableName); for (Pair pair: pairs) { // May not be a server for a particular row if (pair.getSecond() == null) continue; HRegionInfo r = pair.getFirst(); // check for parents if (r.isSplitParent()) continue; // if a split point given, only split that particular region if (r.getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID || (splitPoint != null && !r.containsRow(splitPoint))) continue; // call out to region server to do split now split(pair.getSecond(), pair.getFirst(), splitPoint); } } finally { if (zookeeper != null) { zookeeper.close(); } } } /** * {@inheritDoc} */ @Override public void splitRegion(final byte[] regionName, final byte [] splitPoint) throws IOException { Pair regionServerPair = getRegion(regionName); if (regionServerPair == null) { throw new IllegalArgumentException("Invalid region: " + Bytes.toStringBinary(regionName)); } if (regionServerPair.getFirst() != null && regionServerPair.getFirst().getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID) { throw new IllegalArgumentException("Can't split replicas directly. " + "Replicas are auto-split when their primary is split."); } if (regionServerPair.getSecond() == null) { throw new NoServerForRegionException(Bytes.toStringBinary(regionName)); } split(regionServerPair.getSecond(), regionServerPair.getFirst(), splitPoint); } /** * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName, * byte[])} or {@link #splitRegion(byte[], byte[])} instead. */ @Deprecated public void split(final String tableNameOrRegionName, final String splitPoint) throws IOException { split(Bytes.toBytes(tableNameOrRegionName), Bytes.toBytes(splitPoint)); } /** * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName, * byte[])} or {@link #splitRegion(byte[], byte[])} instead. */ @Deprecated public void split(final byte[] tableNameOrRegionName, final byte [] splitPoint) throws IOException { try { splitRegion(tableNameOrRegionName, splitPoint); } catch (IllegalArgumentException e) { // Bad region, try table split(TableName.valueOf(tableNameOrRegionName), splitPoint); } } @VisibleForTesting public void split(final ServerName sn, final HRegionInfo hri, byte[] splitPoint) throws IOException { if (hri.getStartKey() != null && splitPoint != null && Bytes.compareTo(hri.getStartKey(), splitPoint) == 0) { throw new IOException("should not give a splitkey which equals to startkey!"); } HBaseRpcController controller = rpcControllerFactory.newController(); controller.setPriority(hri.getTable()); // TODO: this does not do retries, it should. Set priority and timeout in controller AdminService.BlockingInterface admin = this.connection.getAdmin(sn); ProtobufUtil.split(controller, admin, hri, splitPoint); } /** * Modify an existing table, more IRB friendly version. * Asynchronous operation. This means that it may be a while before your * schema change is updated across all of the table. * * @param tableName name of table. * @param htd modified description of the table * @throws IOException if a remote or network exception occurs */ @Override public void modifyTable(final TableName tableName, final HTableDescriptor htd) throws IOException { if (!tableName.equals(htd.getTableName())) { throw new IllegalArgumentException("the specified table name '" + tableName + "' doesn't match with the HTD one: " + htd.getTableName()); } executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); controller.setPriority(tableName); ModifyTableRequest request = RequestConverter.buildModifyTableRequest( tableName, htd, ng.getNonceGroup(), ng.newNonce()); master.modifyTable(controller, request); return null; } }); } public void modifyTable(final byte[] tableName, final HTableDescriptor htd) throws IOException { modifyTable(TableName.valueOf(tableName), htd); } public void modifyTable(final String tableName, final HTableDescriptor htd) throws IOException { modifyTable(TableName.valueOf(tableName), htd); } /** * @param regionName Name of a region. * @return a pair of HRegionInfo and ServerName if regionName is * a verified region name (we call {@link * MetaTableAccessor#getRegion(HConnection, byte[])} * else null. * Throw IllegalArgumentException if regionName is null. * @throws IOException if a remote or network exception occurs */ Pair getRegion(final byte[] regionName) throws IOException { if (regionName == null) { throw new IllegalArgumentException("Pass a table name or region name"); } Pair pair = MetaTableAccessor.getRegion(connection, regionName); if (pair == null) { final AtomicReference> result = new AtomicReference>(null); final String encodedName = Bytes.toString(regionName); MetaScannerVisitor visitor = new MetaScannerVisitorBase() { @Override public boolean processRow(Result data) throws IOException { HRegionInfo info = HRegionInfo.getHRegionInfo(data); if (info == null) { LOG.warn("No serialized HRegionInfo in " + data); return true; } RegionLocations rl = MetaTableAccessor.getRegionLocations(data); boolean matched = false; ServerName sn = null; for (HRegionLocation h : rl.getRegionLocations()) { if (h != null && encodedName.equals(h.getRegionInfo().getEncodedName())) { sn = h.getServerName(); info = h.getRegionInfo(); matched = true; } } if (!matched) return true; result.set(new Pair(info, sn)); return false; // found the region, stop } }; MetaScanner.metaScan(connection, visitor, null); pair = result.get(); } return pair; } /** * If the input is a region name, it is returned as is. If it's an * encoded region name, the corresponding region is found from meta * and its region name is returned. If we can't find any region in * meta matching the input as either region name or encoded region * name, the input is returned as is. We don't throw unknown * region exception. */ private byte[] getRegionName( final byte[] regionNameOrEncodedRegionName) throws IOException { if (Bytes.equals(regionNameOrEncodedRegionName, HRegionInfo.FIRST_META_REGIONINFO.getRegionName()) || Bytes.equals(regionNameOrEncodedRegionName, HRegionInfo.FIRST_META_REGIONINFO.getEncodedNameAsBytes())) { return HRegionInfo.FIRST_META_REGIONINFO.getRegionName(); } byte[] tmp = regionNameOrEncodedRegionName; Pair regionServerPair = getRegion(regionNameOrEncodedRegionName); if (regionServerPair != null && regionServerPair.getFirst() != null) { tmp = regionServerPair.getFirst().getRegionName(); } return tmp; } /** * Check if table exists or not * @param tableName Name of a table. * @return tableName instance * @throws IOException if a remote or network exception occurs. * @throws TableNotFoundException if table does not exist. */ private TableName checkTableExists(final TableName tableName) throws IOException { if (!MetaTableAccessor.tableExists(connection, tableName)) { throw new TableNotFoundException(tableName); } return tableName; } /** * Shuts down the HBase cluster * @throws IOException if a remote or network exception occurs */ @Override public synchronized void shutdown() throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); controller.setPriority(HConstants.HIGH_QOS); master.shutdown(controller, ShutdownRequest.newBuilder().build()); return null; } }); } /** * Shuts down the current HBase master only. * Does not shutdown the cluster. * @see #shutdown() * @throws IOException if a remote or network exception occurs */ @Override public synchronized void stopMaster() throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); controller.setPriority(HConstants.HIGH_QOS); master.stopMaster(controller, StopMasterRequest.newBuilder().build()); return null; } }); } /** * Stop the designated regionserver * @param hostnamePort Hostname and port delimited by a : as in * example.org:1234 * @throws IOException if a remote or network exception occurs */ @Override public synchronized void stopRegionServer(final String hostnamePort) throws IOException { String hostname = Addressing.parseHostname(hostnamePort); int port = Addressing.parsePort(hostnamePort); AdminService.BlockingInterface admin = this.connection.getAdmin(ServerName.valueOf(hostname, port, 0)); StopServerRequest request = RequestConverter.buildStopServerRequest( "Called by admin client " + this.connection.toString()); HBaseRpcController controller = rpcControllerFactory.newController(); controller.setPriority(HConstants.HIGH_QOS); try { // TODO: this does not do retries, it should. Set priority and timeout in controller admin.stopServer(controller, request); } catch (ServiceException se) { throw ProtobufUtil.getRemoteException(se); } } /** * @return cluster status * @throws IOException if a remote or network exception occurs */ @Override public boolean isMasterInMaintenanceMode() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public IsInMaintenanceModeResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.isMasterInMaintenanceMode( controller, IsInMaintenanceModeRequest.newBuilder().build()); } }).getInMaintenanceMode(); } @Override public ClusterStatus getClusterStatus() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public ClusterStatus call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); GetClusterStatusRequest req = RequestConverter.buildGetClusterStatusRequest(); return ClusterStatus.convert(master.getClusterStatus(controller, req).getClusterStatus()); } }); } /** * @return Configuration used by the instance. */ @Override public Configuration getConfiguration() { return this.conf; } /** * Create a new namespace * @param descriptor descriptor which describes the new namespace * @throws IOException if a remote or network exception occurs */ @Override public void createNamespace(final NamespaceDescriptor descriptor) throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); // TODO: set priority based on NS? master.createNamespace(controller, CreateNamespaceRequest.newBuilder() .setNamespaceDescriptor(ProtobufUtil .toProtoNamespaceDescriptor(descriptor)).build() ); return null; } }); } /** * Modify an existing namespace * @param descriptor descriptor which describes the new namespace * @throws IOException if a remote or network exception occurs */ @Override public void modifyNamespace(final NamespaceDescriptor descriptor) throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); master.modifyNamespace(controller, ModifyNamespaceRequest.newBuilder(). setNamespaceDescriptor(ProtobufUtil.toProtoNamespaceDescriptor(descriptor)).build()); return null; } }); } /** * Delete an existing namespace. Only empty namespaces (no tables) can be removed. * @param name namespace name * @throws IOException if a remote or network exception occurs */ @Override public void deleteNamespace(final String name) throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); master.deleteNamespace(controller, DeleteNamespaceRequest.newBuilder(). setNamespaceName(name).build()); return null; } }); } /** * Get a namespace descriptor by name * @param name name of namespace descriptor * @return A descriptor * @throws IOException if a remote or network exception occurs */ @Override public NamespaceDescriptor getNamespaceDescriptor(final String name) throws NamespaceNotFoundException, IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public NamespaceDescriptor call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return ProtobufUtil.toNamespaceDescriptor( master.getNamespaceDescriptor(controller, GetNamespaceDescriptorRequest.newBuilder(). setNamespaceName(name).build()).getNamespaceDescriptor()); } }); } /** * List available namespaces * @return List of namespace names * @throws IOException if a remote or network exception occurs */ @Override public String[] listNamespaces() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public String[] call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); List list = master.listNamespaces(controller, ListNamespacesRequest.newBuilder().build()).getNamespaceNameList(); return list.toArray(new String[list.size()]); } }); } /** * List available namespace descriptors * @return List of descriptors * @throws IOException if a remote or network exception occurs */ @Override public NamespaceDescriptor[] listNamespaceDescriptors() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public NamespaceDescriptor[] call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); List list = master.listNamespaceDescriptors(controller, ListNamespaceDescriptorsRequest.newBuilder().build()) .getNamespaceDescriptorList(); NamespaceDescriptor[] res = new NamespaceDescriptor[list.size()]; for(int i = 0; i < list.size(); i++) { res[i] = ProtobufUtil.toNamespaceDescriptor(list.get(i)); } return res; } }); } /** * List procedures * @return procedure list * @throws IOException if a remote or network exception occurs */ @Override public ProcedureInfo[] listProcedures() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public ProcedureInfo[] call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); List procList = master.listProcedures( controller, ListProceduresRequest.newBuilder().build()).getProcedureList(); ProcedureInfo[] procInfoList = new ProcedureInfo[procList.size()]; for (int i = 0; i < procList.size(); i++) { procInfoList[i] = ProcedureInfo.convert(procList.get(i)); } return procInfoList; } }); } /** * Get list of table descriptors by namespace * @param name namespace name * @return A descriptor * @throws IOException if a remote or network exception occurs */ @Override public HTableDescriptor[] listTableDescriptorsByNamespace(final String name) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public HTableDescriptor[] call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); List list = master.listTableDescriptorsByNamespace(controller, ListTableDescriptorsByNamespaceRequest.newBuilder().setNamespaceName(name) .build()).getTableSchemaList(); HTableDescriptor[] res = new HTableDescriptor[list.size()]; for(int i=0; i < list.size(); i++) { res[i] = HTableDescriptor.convert(list.get(i)); } return res; } }); } /** * Get list of table names by namespace * @param name namespace name * @return The list of table names in the namespace * @throws IOException if a remote or network exception occurs */ @Override public TableName[] listTableNamesByNamespace(final String name) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public TableName[] call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); List tableNames = master.listTableNamesByNamespace(controller, ListTableNamesByNamespaceRequest. newBuilder().setNamespaceName(name).build()) .getTableNameList(); TableName[] result = new TableName[tableNames.size()]; for (int i = 0; i < tableNames.size(); i++) { result[i] = ProtobufUtil.toTableName(tableNames.get(i)); } return result; } }); } /** * Check to see if HBase is running. Throw an exception if not. * @param conf system configuration * @throws MasterNotRunningException if the master is not running * @throws ZooKeeperConnectionException if unable to connect to zookeeper */ // Used by tests and by the Merge tool. Merge tool uses it to figure if HBase is up or not. public static void checkHBaseAvailable(Configuration conf) throws MasterNotRunningException, ZooKeeperConnectionException, ServiceException, IOException { Configuration copyOfConf = HBaseConfiguration.create(conf); // We set it to make it fail as soon as possible if HBase is not available copyOfConf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1); copyOfConf.setInt("zookeeper.recovery.retry", 0); try (ClusterConnection connection = (ClusterConnection)ConnectionFactory.createConnection(copyOfConf)) { // Check ZK first. // If the connection exists, we may have a connection to ZK that does not work anymore ZooKeeperKeepAliveConnection zkw = null; try { // This is NASTY. FIX!!!! Dependent on internal implementation! TODO zkw = ((ConnectionManager.HConnectionImplementation)connection). getKeepAliveZooKeeperWatcher(); zkw.getRecoverableZooKeeper().getZooKeeper().exists(zkw.baseZNode, false); } catch (IOException e) { throw new ZooKeeperConnectionException("Can't connect to ZooKeeper", e); } catch (InterruptedException e) { throw (InterruptedIOException) new InterruptedIOException("Can't connect to ZooKeeper").initCause(e); } catch (KeeperException e) { throw new ZooKeeperConnectionException("Can't connect to ZooKeeper", e); } finally { if (zkw != null) { zkw.close(); } } connection.isMasterRunning(); } } /** * get the regions of a given table. * * @param tableName the name of the table * @return Ordered list of {@link HRegionInfo}. * @throws IOException if a remote or network exception occurs */ @Override public List getTableRegions(final TableName tableName) throws IOException { ZooKeeperWatcher zookeeper = new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(), new ThrowableAbortable()); List Regions = null; try { Regions = MetaTableAccessor.getTableRegions(zookeeper, connection, tableName, true); } finally { zookeeper.close(); } return Regions; } public List getTableRegions(final byte[] tableName) throws IOException { return getTableRegions(TableName.valueOf(tableName)); } @Override public synchronized void close() throws IOException { if (cleanupConnectionOnClose && this.connection != null && !this.closed) { this.connection.close(); this.closed = true; } } /** * Get tableDescriptors * @param tableNames List of table names * @return HTD[] the tableDescriptor * @throws IOException if a remote or network exception occurs */ @Override public HTableDescriptor[] getTableDescriptorsByTableName(final List tableNames) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public HTableDescriptor[] call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); GetTableDescriptorsRequest req = RequestConverter.buildGetTableDescriptorsRequest(tableNames); return ProtobufUtil.getHTableDescriptorArray(master.getTableDescriptors(controller, req)); } }); } /** * Get tableDescriptor * @param tableName one table name * @return HTD the HTableDescriptor or null if the table not exists * @throws IOException if a remote or network exception occurs */ private HTableDescriptor getTableDescriptorByTableName(TableName tableName) throws IOException { List tableNames = new ArrayList(1); tableNames.add(tableName); HTableDescriptor[] htdl = getTableDescriptorsByTableName(tableNames); if (htdl == null || htdl.length == 0) { return null; } else { return htdl[0]; } } /** * Get tableDescriptors * @param names List of table names * @return HTD[] the tableDescriptor * @throws IOException if a remote or network exception occurs */ @Override public HTableDescriptor[] getTableDescriptors(List names) throws IOException { List tableNames = new ArrayList(names.size()); for(String name : names) { tableNames.add(TableName.valueOf(name)); } return getTableDescriptorsByTableName(tableNames); } private RollWALWriterResponse rollWALWriterImpl(final ServerName sn) throws IOException, FailedLogCloseException { AdminService.BlockingInterface admin = this.connection.getAdmin(sn); RollWALWriterRequest request = RequestConverter.buildRollWALWriterRequest(); HBaseRpcController controller = rpcControllerFactory.newController(); try { // TODO: this does not do retries, it should. Set priority and timeout in controller return admin.rollWALWriter(controller, request); } catch (ServiceException se) { throw ProtobufUtil.getRemoteException(se); } } /** * Roll the log writer. I.e. when using a file system based write ahead log, * start writing log messages to a new file. * * Note that when talking to a version 1.0+ HBase deployment, the rolling is asynchronous. * This method will return as soon as the roll is requested and the return value will * always be null. Additionally, the named region server may schedule store flushes at the * request of the wal handling the roll request. * * When talking to a 0.98 or older HBase deployment, the rolling is synchronous and the * return value may be either null or a list of encoded region names. * * @param serverName * The servername of the regionserver. A server name is made of host, * port and startcode. This is mandatory. Here is an example: * host187.example.com,60020,1289493121758 * @return a set of {@link HRegionInfo#getEncodedName()} that would allow the wal to * clean up some underlying files. null if there's nothing to flush. * @throws IOException if a remote or network exception occurs * @throws FailedLogCloseException * @deprecated use {@link #rollWALWriter(ServerName)} */ @Deprecated public synchronized byte[][] rollHLogWriter(String serverName) throws IOException, FailedLogCloseException { ServerName sn = ServerName.valueOf(serverName); final RollWALWriterResponse response = rollWALWriterImpl(sn); int regionCount = response.getRegionToFlushCount(); if (0 == regionCount) { return null; } byte[][] regionsToFlush = new byte[regionCount][]; for (int i = 0; i < regionCount; i++) { ByteString region = response.getRegionToFlush(i); regionsToFlush[i] = region.toByteArray(); } return regionsToFlush; } @Override public synchronized void rollWALWriter(ServerName serverName) throws IOException, FailedLogCloseException { rollWALWriterImpl(serverName); } @Override public String[] getMasterCoprocessors() { try { return getClusterStatus().getMasterCoprocessors(); } catch (IOException e) { LOG.error("Could not getClusterStatus()",e); return null; } } /** * {@inheritDoc} */ @Override public CompactionState getCompactionState(final TableName tableName) throws IOException { CompactionState state = CompactionState.NONE; ZooKeeperWatcher zookeeper = new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(), new ThrowableAbortable()); try { checkTableExists(tableName); List> pairs = MetaTableAccessor.getTableRegionsAndLocations(zookeeper, connection, tableName); for (Pair pair: pairs) { if (pair.getFirst().isOffline()) continue; if (pair.getSecond() == null) continue; try { ServerName sn = pair.getSecond(); AdminService.BlockingInterface admin = this.connection.getAdmin(sn); GetRegionInfoRequest request = RequestConverter.buildGetRegionInfoRequest( pair.getFirst().getRegionName(), true); GetRegionInfoResponse response = admin.getRegionInfo(null, request); switch (response.getCompactionState()) { case MAJOR_AND_MINOR: return CompactionState.MAJOR_AND_MINOR; case MAJOR: if (state == CompactionState.MINOR) { return CompactionState.MAJOR_AND_MINOR; } state = CompactionState.MAJOR; break; case MINOR: if (state == CompactionState.MAJOR) { return CompactionState.MAJOR_AND_MINOR; } state = CompactionState.MINOR; break; case NONE: default: // nothing, continue } } catch (NotServingRegionException e) { if (LOG.isDebugEnabled()) { LOG.debug("Trying to get compaction state of " + pair.getFirst() + ": " + StringUtils.stringifyException(e)); } } catch (RemoteException e) { if (e.getMessage().indexOf(NotServingRegionException.class.getName()) >= 0) { if (LOG.isDebugEnabled()) { LOG.debug("Trying to get compaction state of " + pair.getFirst() + ": " + StringUtils.stringifyException(e)); } } else { throw e; } } } } catch (ServiceException se) { throw ProtobufUtil.getRemoteException(se); } finally { zookeeper.close(); } return state; } /** * {@inheritDoc} */ @Override public CompactionState getCompactionStateForRegion(final byte[] regionName) throws IOException { try { Pair regionServerPair = getRegion(regionName); if (regionServerPair == null) { throw new IllegalArgumentException("Invalid region: " + Bytes.toStringBinary(regionName)); } if (regionServerPair.getSecond() == null) { throw new NoServerForRegionException(Bytes.toStringBinary(regionName)); } ServerName sn = regionServerPair.getSecond(); AdminService.BlockingInterface admin = this.connection.getAdmin(sn); GetRegionInfoRequest request = RequestConverter.buildGetRegionInfoRequest( regionServerPair.getFirst().getRegionName(), true); HBaseRpcController controller = rpcControllerFactory.newController(); // TODO: this does not do retries, it should. Set priority and timeout in controller GetRegionInfoResponse response = admin.getRegionInfo(controller, request); return response.getCompactionState(); } catch (ServiceException se) { throw ProtobufUtil.getRemoteException(se); } } /** * @deprecated Use {@link #getCompactionState(org.apache.hadoop.hbase.TableName)} or {@link * #getCompactionStateForRegion(byte[])} instead. */ @Deprecated public CompactionState getCompactionState(final String tableNameOrRegionName) throws IOException, InterruptedException { return getCompactionState(Bytes.toBytes(tableNameOrRegionName)); } /** * @deprecated Use {@link #getCompactionState(org.apache.hadoop.hbase.TableName)} or {@link * #getCompactionStateForRegion(byte[])} instead. */ @Deprecated public CompactionState getCompactionState(final byte[] tableNameOrRegionName) throws IOException, InterruptedException { try { return getCompactionStateForRegion(tableNameOrRegionName); } catch (IllegalArgumentException e) { // Invalid region, try table return getCompactionState(TableName.valueOf(tableNameOrRegionName)); } } /** * Take a snapshot for the given table. If the table is enabled, a FLUSH-type snapshot will be * taken. If the table is disabled, an offline snapshot is taken. *

* Snapshots are considered unique based on the name of the snapshot. Attempts to take a * snapshot with the same name (even a different type or with different parameters) will fail with * a {@link SnapshotCreationException} indicating the duplicate naming. *

* Snapshot names follow the same naming constraints as tables in HBase. See * {@link org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}. * @param snapshotName name of the snapshot to be created * @param tableName name of the table for which snapshot is created * @throws IOException if a remote or network exception occurs * @throws SnapshotCreationException if snapshot creation failed * @throws IllegalArgumentException if the snapshot request is formatted incorrectly */ @Override public void snapshot(final String snapshotName, final TableName tableName) throws IOException, SnapshotCreationException, IllegalArgumentException { snapshot(snapshotName, tableName, SnapshotDescription.Type.FLUSH); } public void snapshot(final String snapshotName, final String tableName) throws IOException, SnapshotCreationException, IllegalArgumentException { snapshot(snapshotName, TableName.valueOf(tableName), SnapshotDescription.Type.FLUSH); } /** * Create snapshot for the given table of given flush type. *

* Snapshots are considered unique based on the name of the snapshot. Attempts to take a * snapshot with the same name (even a different type or with different parameters) will fail * with a {@link SnapshotCreationException} indicating the duplicate naming. *

* Snapshot names follow the same naming constraints as tables in HBase. * @param snapshotName name of the snapshot to be created * @param tableName name of the table for which snapshot is created * @param flushType if the snapshot should be taken without flush memstore first * @throws IOException if a remote or network exception occurs * @throws SnapshotCreationException if snapshot creation failed * @throws IllegalArgumentException if the snapshot request is formatted incorrectly */ public void snapshot(final byte[] snapshotName, final byte[] tableName, final SnapshotDescription.Type flushType) throws IOException, SnapshotCreationException, IllegalArgumentException { snapshot(Bytes.toString(snapshotName), Bytes.toString(tableName), flushType); } /** * Create snapshot for the given table of given flush type. *

* Snapshots are considered unique based on the name of the snapshot. Attempts to take a * snapshot with the same name (even a different type or with different parameters) will fail * with a {@link SnapshotCreationException} indicating the duplicate naming. *

* Snapshot names follow the same naming constraints as tables in HBase. * @param snapshotName name of the snapshot to be created * @param tableName name of the table for which snapshot is created * @param flushType if the snapshot should be taken without flush memstore first * @param snapshotProps snapshot parameters * @throws IOException if a remote or network exception occurs * @throws SnapshotCreationException if snapshot creation failed * @throws IllegalArgumentException if the snapshot request is formatted incorrectly */ public void snapshot(final byte[] snapshotName, final byte[] tableName, final SnapshotDescription.Type flushType, Map snapshotProps) throws IOException, SnapshotCreationException, IllegalArgumentException { snapshot(Bytes.toString(snapshotName), TableName.valueOf(tableName), flushType, snapshotProps); } /** public void snapshot(final String snapshotName, * Create a timestamp consistent snapshot for the given table. final byte[] tableName) throws IOException, *

* Snapshots are considered unique based on the name of the snapshot. Attempts to take a * snapshot with the same name (even a different type or with different parameters) will fail with * a {@link SnapshotCreationException} indicating the duplicate naming. *

* Snapshot names follow the same naming constraints as tables in HBase. * @param snapshotName name of the snapshot to be created * @param tableName name of the table for which snapshot is created * @throws IOException if a remote or network exception occurs * @throws SnapshotCreationException if snapshot creation failed * @throws IllegalArgumentException if the snapshot request is formatted incorrectly */ @Override public void snapshot(final byte[] snapshotName, final TableName tableName) throws IOException, SnapshotCreationException, IllegalArgumentException { snapshot(Bytes.toString(snapshotName), tableName, SnapshotDescription.Type.FLUSH); } public void snapshot(final byte[] snapshotName, final byte[] tableName) throws IOException, SnapshotCreationException, IllegalArgumentException { snapshot(Bytes.toString(snapshotName), TableName.valueOf(tableName), SnapshotDescription.Type.FLUSH); } /** * Create typed snapshot of the table. *

* Snapshots are considered unique based on the name of the snapshot. Attempts to take a * snapshot with the same name (even a different type or with different parameters) will fail with * a {@link SnapshotCreationException} indicating the duplicate naming. *

* Snapshot names follow the same naming constraints as tables in HBase. See * {@link org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}. *

* @param snapshotName name to give the snapshot on the filesystem. Must be unique from all other * snapshots stored on the cluster * @param tableName name of the table to snapshot * @param type type of snapshot to take * @param snapshotProps snapshot parameters * @throws IOException we fail to reach the master * @throws SnapshotCreationException if snapshot creation failed * @throws IllegalArgumentException if the snapshot request is formatted incorrectly */ @Override public void snapshot(final String snapshotName, final TableName tableName, SnapshotDescription.Type type, Map snapshotProps) throws IOException, SnapshotCreationException, IllegalArgumentException { SnapshotDescription.Builder builder = SnapshotDescription.newBuilder(); builder.setTable(tableName.getNameAsString()); builder.setName(snapshotName); builder.setType(type); builder.setTtl(getTtlFromSnapshotProps(snapshotProps)); snapshot(builder.build()); } private long getTtlFromSnapshotProps(Map snapshotProps) { return MapUtils.getLongValue(snapshotProps, "TTL", -1); } public void snapshot(final String snapshotName, final TableName tableName, SnapshotDescription.Type type) throws IOException, SnapshotCreationException, IllegalArgumentException { snapshot(snapshotName, tableName, type, null); } public void snapshot(final String snapshotName, final String tableName, SnapshotDescription.Type type) throws IOException, SnapshotCreationException, IllegalArgumentException { snapshot(snapshotName, TableName.valueOf(tableName), type, null); } public void snapshot(final String snapshotName, final byte[] tableName, SnapshotDescription.Type type) throws IOException, SnapshotCreationException, IllegalArgumentException { snapshot(snapshotName, TableName.valueOf(tableName), type, null); } /** * Take a snapshot and wait for the server to complete that snapshot (blocking). *

* Only a single snapshot should be taken at a time for an instance of HBase, or results may be * undefined (you can tell multiple HBase clusters to snapshot at the same time, but only one at a * time for a single cluster). *

* Snapshots are considered unique based on the name of the snapshot. Attempts to take a * snapshot with the same name (even a different type or with different parameters) will fail with * a {@link SnapshotCreationException} indicating the duplicate naming. *

* Snapshot names follow the same naming constraints as tables in HBase. See * {@link org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}. *

* You should probably use {@link #snapshot(String, String)} or {@link #snapshot(byte[], byte[])} * unless you are sure about the type of snapshot that you want to take. * @param snapshot snapshot to take * @throws IOException or we lose contact with the master. * @throws SnapshotCreationException if snapshot failed to be taken * @throws IllegalArgumentException if the snapshot request is formatted incorrectly */ @Override public void snapshot(SnapshotDescription snapshot) throws IOException, SnapshotCreationException, IllegalArgumentException { // actually take the snapshot SnapshotResponse response = takeSnapshotAsync(snapshot); final IsSnapshotDoneRequest request = IsSnapshotDoneRequest.newBuilder().setSnapshot(snapshot) .build(); IsSnapshotDoneResponse done = null; long start = EnvironmentEdgeManager.currentTime(); long max = response.getExpectedTimeout(); long maxPauseTime = max / this.numRetries; int tries = 0; LOG.debug("Waiting a max of " + max + " ms for snapshot '" + ClientSnapshotDescriptionUtils.toString(snapshot) + "'' to complete. (max " + maxPauseTime + " ms per retry)"); while (tries == 0 || ((EnvironmentEdgeManager.currentTime() - start) < max && !done.getDone())) { try { // sleep a backoff <= pauseTime amount long sleep = getPauseTime(tries++); sleep = sleep > maxPauseTime ? maxPauseTime : sleep; LOG.debug("(#" + tries + ") Sleeping: " + sleep + "ms while waiting for snapshot completion."); Thread.sleep(sleep); } catch (InterruptedException e) { throw (InterruptedIOException)new InterruptedIOException("Interrupted").initCause(e); } LOG.debug("Getting current status of snapshot from master..."); done = executeCallable(new MasterCallable(getConnection()) { @Override public IsSnapshotDoneResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.isSnapshotDone(controller, request); } }); } if (!done.getDone()) { throw new SnapshotCreationException("Snapshot '" + snapshot.getName() + "' wasn't completed in expectedTime:" + max + " ms", snapshot); } } /** * Take a snapshot without waiting for the server to complete that snapshot (asynchronous) *

* Only a single snapshot should be taken at a time, or results may be undefined. * @param snapshot snapshot to take * @return response from the server indicating the max time to wait for the snapshot * @throws IOException if the snapshot did not succeed or we lose contact with the master. * @throws SnapshotCreationException if snapshot creation failed * @throws IllegalArgumentException if the snapshot request is formatted incorrectly */ @Override public SnapshotResponse takeSnapshotAsync(SnapshotDescription snapshot) throws IOException, SnapshotCreationException { ClientSnapshotDescriptionUtils.assertSnapshotRequestIsValid(snapshot); final SnapshotRequest request = SnapshotRequest.newBuilder().setSnapshot(snapshot) .build(); // run the snapshot on the master return executeCallable(new MasterCallable(getConnection()) { @Override public SnapshotResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.snapshot(controller, request); } }); } /** * Check the current state of the passed snapshot. *

* There are three possible states: *

    *
  1. running - returns false
  2. *
  3. finished - returns true
  4. *
  5. finished with error - throws the exception that caused the snapshot to fail
  6. *
*

* The cluster only knows about the most recent snapshot. Therefore, if another snapshot has been * run/started since the snapshot your are checking, you will recieve an * {@link UnknownSnapshotException}. * @param snapshot description of the snapshot to check * @return true if the snapshot is completed, false if the snapshot is still * running * @throws IOException if we have a network issue * @throws HBaseSnapshotException if the snapshot failed * @throws UnknownSnapshotException if the requested snapshot is unknown */ @Override public boolean isSnapshotFinished(final SnapshotDescription snapshot) throws IOException, HBaseSnapshotException, UnknownSnapshotException { return executeCallable(new MasterCallable(getConnection()) { @Override public IsSnapshotDoneResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.isSnapshotDone(controller, IsSnapshotDoneRequest.newBuilder().setSnapshot(snapshot).build()); } }).getDone(); } /** * Restore the specified snapshot on the original table. (The table must be disabled) * If the "hbase.snapshot.restore.take.failsafe.snapshot" configuration property * is set to true, a snapshot of the current table is taken * before executing the restore operation. * In case of restore failure, the failsafe snapshot will be restored. * If the restore completes without problem the failsafe snapshot is deleted. * * @param snapshotName name of the snapshot to restore * @throws IOException if a remote or network exception occurs * @throws RestoreSnapshotException if snapshot failed to be restored * @throws IllegalArgumentException if the restore request is formatted incorrectly */ @Override public void restoreSnapshot(final byte[] snapshotName) throws IOException, RestoreSnapshotException { restoreSnapshot(Bytes.toString(snapshotName)); } /** * Restore the specified snapshot on the original table. (The table must be disabled) * If the "hbase.snapshot.restore.take.failsafe.snapshot" configuration property * is set to true, a snapshot of the current table is taken * before executing the restore operation. * In case of restore failure, the failsafe snapshot will be restored. * If the restore completes without problem the failsafe snapshot is deleted. * * @param snapshotName name of the snapshot to restore * @throws IOException if a remote or network exception occurs * @throws RestoreSnapshotException if snapshot failed to be restored * @throws IllegalArgumentException if the restore request is formatted incorrectly */ @Override public void restoreSnapshot(final String snapshotName) throws IOException, RestoreSnapshotException { boolean takeFailSafeSnapshot = conf.getBoolean("hbase.snapshot.restore.take.failsafe.snapshot", false); restoreSnapshot(snapshotName, takeFailSafeSnapshot); } /** * Restore the specified snapshot on the original table. (The table must be disabled) * If 'takeFailSafeSnapshot' is set to true, a snapshot of the current table is taken * before executing the restore operation. * In case of restore failure, the failsafe snapshot will be restored. * If the restore completes without problem the failsafe snapshot is deleted. * * The failsafe snapshot name is configurable by using the property * "hbase.snapshot.restore.failsafe.name". * * @param snapshotName name of the snapshot to restore * @param takeFailSafeSnapshot true if the failsafe snapshot should be taken * @throws IOException if a remote or network exception occurs * @throws RestoreSnapshotException if snapshot failed to be restored * @throws IllegalArgumentException if the restore request is formatted incorrectly */ @Override public void restoreSnapshot(final byte[] snapshotName, final boolean takeFailSafeSnapshot) throws IOException, RestoreSnapshotException { restoreSnapshot(Bytes.toString(snapshotName), takeFailSafeSnapshot); } /** * Restore the specified snapshot on the original table. (The table must be disabled) If * 'takeFailSafeSnapshot' is set to true, a snapshot of the current table is taken before * executing the restore operation. In case of restore failure, the failsafe snapshot will be * restored. If the restore completes without problem the failsafe snapshot is deleted. The * failsafe snapshot name is configurable by using the property * "hbase.snapshot.restore.failsafe.name". * @param snapshotName name of the snapshot to restore * @param takeFailSafeSnapshot true if the failsafe snapshot should be taken * @param restoreAcl true to restore acl of snapshot into table. * @throws IOException if a remote or network exception occurs * @throws RestoreSnapshotException if snapshot failed to be restored * @throws IllegalArgumentException if the restore request is formatted incorrectly */ @Override public void restoreSnapshot(final String snapshotName, boolean takeFailSafeSnapshot, boolean restoreAcl) throws IOException, RestoreSnapshotException { TableName tableName = null; for (SnapshotDescription snapshotInfo: listSnapshots()) { if (snapshotInfo.getName().equals(snapshotName)) { tableName = TableName.valueOf(snapshotInfo.getTable()); break; } } if (tableName == null) { throw new RestoreSnapshotException( "Unable to find the table name for snapshot=" + snapshotName); } // The table does not exists, switch to clone. if (!tableExists(tableName)) { cloneSnapshot(snapshotName, tableName, restoreAcl); return; } // Check if the table is disabled if (!isTableDisabled(tableName)) { throw new TableNotDisabledException(tableName); } // Take a snapshot of the current state String failSafeSnapshotSnapshotName = null; if (takeFailSafeSnapshot) { failSafeSnapshotSnapshotName = conf.get("hbase.snapshot.restore.failsafe.name", "hbase-failsafe-{snapshot.name}-{restore.timestamp}"); failSafeSnapshotSnapshotName = failSafeSnapshotSnapshotName .replace("{snapshot.name}", snapshotName) .replace("{table.name}", tableName.toString().replace(TableName.NAMESPACE_DELIM, '.')) .replace("{restore.timestamp}", String.valueOf(EnvironmentEdgeManager.currentTime())); LOG.info("Taking restore-failsafe snapshot: " + failSafeSnapshotSnapshotName); snapshot(failSafeSnapshotSnapshotName, tableName); } try { // Restore snapshot internalRestoreSnapshot(snapshotName, tableName, restoreAcl); } catch (IOException e) { // Somthing went wrong during the restore... // if the pre-restore snapshot is available try to rollback if (takeFailSafeSnapshot) { try { internalRestoreSnapshot(failSafeSnapshotSnapshotName, tableName, restoreAcl); String msg = "Restore snapshot=" + snapshotName + " failed. Rollback to snapshot=" + failSafeSnapshotSnapshotName + " succeeded."; LOG.error(msg, e); throw new RestoreSnapshotException(msg, e); } catch (IOException ex) { String msg = "Failed to restore and rollback to snapshot=" + failSafeSnapshotSnapshotName; LOG.error(msg, ex); throw new RestoreSnapshotException(msg, e); } } else { throw new RestoreSnapshotException("Failed to restore snapshot=" + snapshotName, e); } } // If the restore is succeeded, delete the pre-restore snapshot if (takeFailSafeSnapshot) { try { LOG.info("Deleting restore-failsafe snapshot: " + failSafeSnapshotSnapshotName); deleteSnapshot(failSafeSnapshotSnapshotName); } catch (IOException e) { LOG.error("Unable to remove the failsafe snapshot: " + failSafeSnapshotSnapshotName, e); } } } @Override public void restoreSnapshot(String snapshotName, boolean takeFailSafeSnapshot) throws IOException, RestoreSnapshotException { restoreSnapshot(snapshotName, takeFailSafeSnapshot, false); } /** * Create a new table by cloning the snapshot content. * * @param snapshotName name of the snapshot to be cloned * @param tableName name of the table where the snapshot will be restored * @throws IOException if a remote or network exception occurs * @throws TableExistsException if table to be created already exists * @throws RestoreSnapshotException if snapshot failed to be cloned * @throws IllegalArgumentException if the specified table has not a valid name */ public void cloneSnapshot(final byte[] snapshotName, final byte[] tableName) throws IOException, TableExistsException, RestoreSnapshotException { cloneSnapshot(Bytes.toString(snapshotName), TableName.valueOf(tableName)); } /** * Create a new table by cloning the snapshot content. * * @param snapshotName name of the snapshot to be cloned * @param tableName name of the table where the snapshot will be restored * @throws IOException if a remote or network exception occurs * @throws TableExistsException if table to be created already exists * @throws RestoreSnapshotException if snapshot failed to be cloned * @throws IllegalArgumentException if the specified table has not a valid name */ @Override public void cloneSnapshot(final byte[] snapshotName, final TableName tableName) throws IOException, TableExistsException, RestoreSnapshotException { cloneSnapshot(Bytes.toString(snapshotName), tableName); } /** * Create a new table by cloning the snapshot content. * * @param snapshotName name of the snapshot to be cloned * @param tableName name of the table where the snapshot will be restored * @throws IOException if a remote or network exception occurs * @throws TableExistsException if table to be created already exists * @throws RestoreSnapshotException if snapshot failed to be cloned * @throws IllegalArgumentException if the specified table has not a valid name */ public void cloneSnapshot(final String snapshotName, final String tableName) throws IOException, TableExistsException, RestoreSnapshotException, InterruptedException { cloneSnapshot(snapshotName, TableName.valueOf(tableName)); } /** * Create a new table by cloning the snapshot content. * * @param snapshotName name of the snapshot to be cloned * @param tableName name of the table where the snapshot will be restored * @throws IOException if a remote or network exception occurs * @throws TableExistsException if table to be created already exists * @throws RestoreSnapshotException if snapshot failed to be cloned * @throws IllegalArgumentException if the specified table has not a valid name */ @Override public void cloneSnapshot(final String snapshotName, final TableName tableName, final boolean restoreAcl) throws IOException, TableExistsException, RestoreSnapshotException { if (tableExists(tableName)) { throw new TableExistsException(tableName); } internalRestoreSnapshot(snapshotName, tableName, restoreAcl); waitUntilTableIsEnabled(tableName); } @Override public void cloneSnapshot(String snapshotName, TableName tableName) throws IOException, TableExistsException, RestoreSnapshotException { cloneSnapshot(snapshotName, tableName, false); } /** * Execute a distributed procedure on a cluster synchronously with return data * * @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. * @throws IOException if a remote or network exception occurs */ @Override public byte[] execProcedureWithRet(String signature, String instance, Map props) throws IOException { ProcedureDescription.Builder builder = ProcedureDescription.newBuilder(); builder.setSignature(signature).setInstance(instance); for (Entry 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(); // run the procedure on the master ExecProcedureResponse response = executeCallable(new MasterCallable( getConnection()) { @Override public ExecProcedureResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.execProcedureWithRet(controller, request); } }); return response.hasReturnData() ? response.getReturnData().toByteArray() : null; } /** * 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 * @throws IOException if a remote or network exception occurs */ @Override public void execProcedure(String signature, String instance, Map props) throws IOException { ProcedureDescription.Builder builder = ProcedureDescription.newBuilder(); builder.setSignature(signature).setInstance(instance); for (Entry 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(); // run the procedure on the master ExecProcedureResponse response = executeCallable(new MasterCallable( getConnection()) { @Override public ExecProcedureResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.execProcedure(controller, request); } }); long start = EnvironmentEdgeManager.currentTime(); long max = response.getExpectedTimeout(); long maxPauseTime = max / this.numRetries; int tries = 0; LOG.debug("Waiting a max of " + max + " ms for procedure '" + signature + " : " + instance + "'' to complete. (max " + maxPauseTime + " ms per retry)"); boolean done = false; while (tries == 0 || ((EnvironmentEdgeManager.currentTime() - start) < max && !done)) { try { // sleep a backoff <= pauseTime amount long sleep = getPauseTime(tries++); sleep = sleep > maxPauseTime ? maxPauseTime : sleep; LOG.debug("(#" + tries + ") Sleeping: " + sleep + "ms while waiting for procedure completion."); Thread.sleep(sleep); } catch (InterruptedException e) { throw (InterruptedIOException)new InterruptedIOException("Interrupted").initCause(e); } LOG.debug("Getting current status of procedure from master..."); done = isProcedureFinished(signature, instance, props); } if (!done) { throw new IOException("Procedure '" + signature + " : " + instance + "' wasn't completed in expectedTime:" + max + " ms"); } } /** * Check the current state of the specified procedure. *

* There are three possible states: *

    *
  1. running - returns false
  2. *
  3. finished - returns true
  4. *
  5. finished with error - throws the exception that caused the procedure to fail
  6. *
*

* * @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 * @throws IOException if the specified procedure finished with error */ @Override public boolean isProcedureFinished(String signature, String instance, Map props) throws IOException { final ProcedureDescription.Builder builder = ProcedureDescription.newBuilder(); builder.setSignature(signature).setInstance(instance); for (Entry entry : props.entrySet()) { NameStringPair pair = NameStringPair.newBuilder().setName(entry.getKey()) .setValue(entry.getValue()).build(); builder.addConfiguration(pair); } final ProcedureDescription desc = builder.build(); return executeCallable( new MasterCallable(getConnection()) { @Override public IsProcedureDoneResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.isProcedureDone(controller, IsProcedureDoneRequest .newBuilder().setProcedure(desc).build()); } }).getDone(); } /** * Execute Restore/Clone snapshot and wait for the server to complete (blocking). To check if the * cloned table exists, use {@link #isTableAvailable} -- it is not safe to create an HTable * instance to this table before it is available. * @param snapshotName snapshot to restore * @param tableName table name to restore the snapshot on * @throws IOException if a remote or network exception occurs * @throws RestoreSnapshotException if snapshot failed to be restored * @throws IllegalArgumentException if the restore request is formatted incorrectly */ private void internalRestoreSnapshot(final String snapshotName, final TableName tableName, final boolean restoreAcl) throws IOException, RestoreSnapshotException { SnapshotDescription snapshot = SnapshotDescription.newBuilder() .setName(snapshotName).setTable(tableName.getNameAsString()).build(); // actually restore the snapshot internalRestoreSnapshotAsync(snapshot, restoreAcl); final IsRestoreSnapshotDoneRequest request = IsRestoreSnapshotDoneRequest.newBuilder() .setSnapshot(snapshot).build(); IsRestoreSnapshotDoneResponse done = IsRestoreSnapshotDoneResponse.newBuilder() .setDone(false).buildPartial(); final long maxPauseTime = 5000; int tries = 0; while (!done.getDone()) { try { // sleep a backoff <= pauseTime amount long sleep = getPauseTime(tries++); sleep = sleep > maxPauseTime ? maxPauseTime : sleep; LOG.debug(tries + ") Sleeping: " + sleep + " ms while we wait for snapshot restore to complete."); Thread.sleep(sleep); } catch (InterruptedException e) { throw (InterruptedIOException)new InterruptedIOException("Interrupted").initCause(e); } LOG.debug("Getting current status of snapshot restore from master..."); done = executeCallable(new MasterCallable( getConnection()) { @Override public IsRestoreSnapshotDoneResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.isRestoreSnapshotDone(controller, request); } }); } if (!done.getDone()) { throw new RestoreSnapshotException("Snapshot '" + snapshot.getName() + "' wasn't restored."); } } /** * Execute Restore/Clone snapshot and wait for the server to complete (asynchronous) *

* Only a single snapshot should be restored at a time, or results may be undefined. * @param snapshot snapshot to restore * @return response from the server indicating the max time to wait for the snapshot * @throws IOException if a remote or network exception occurs * @throws RestoreSnapshotException if snapshot failed to be restored * @throws IllegalArgumentException if the restore request is formatted incorrectly */ private RestoreSnapshotResponse internalRestoreSnapshotAsync(final SnapshotDescription snapshot, final boolean restoreAcl) throws IOException, RestoreSnapshotException { ClientSnapshotDescriptionUtils.assertSnapshotRequestIsValid(snapshot); final RestoreSnapshotRequest request = RestoreSnapshotRequest.newBuilder().setSnapshot(snapshot).setRestoreACL(restoreAcl).build(); // run the snapshot restore on the master return executeCallable(new MasterCallable(getConnection()) { @Override public RestoreSnapshotResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.restoreSnapshot(controller, request); } }); } /** * List completed snapshots. * @return a list of snapshot descriptors for completed snapshots * @throws IOException if a network error occurs */ @Override public List listSnapshots() throws IOException { return executeCallable(new MasterCallable>(getConnection()) { @Override public List call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); return master.getCompletedSnapshots(controller, GetCompletedSnapshotsRequest.newBuilder().build()).getSnapshotsList(); } }); } /** * List all the completed snapshots matching the given regular expression. * * @param regex The regular expression to match against * @return - returns a List of SnapshotDescription * @throws IOException if a remote or network exception occurs */ @Override public List listSnapshots(String regex) throws IOException { return listSnapshots(Pattern.compile(regex)); } /** * List all the completed snapshots matching the given pattern. * * @param pattern The compiled regular expression to match against * @return - returns a List of SnapshotDescription * @throws IOException if a remote or network exception occurs */ @Override public List listSnapshots(Pattern pattern) throws IOException { List matched = new LinkedList(); List snapshots = listSnapshots(); for (SnapshotDescription snapshot : snapshots) { if (pattern.matcher(snapshot.getName()).matches()) { matched.add(snapshot); } } return matched; } /** * List all the completed snapshots matching the given table name regular expression and snapshot * name regular expression. * @param tableNameRegex The table name regular expression to match against * @param snapshotNameRegex The snapshot name regular expression to match against * @return returns a List of completed SnapshotDescription * @throws IOException if a remote or network exception occurs */ @Override public List listTableSnapshots(String tableNameRegex, String snapshotNameRegex) throws IOException { return listTableSnapshots(Pattern.compile(tableNameRegex), Pattern.compile(snapshotNameRegex)); } /** * List all the completed snapshots matching the given table name regular expression and snapshot * name regular expression. * @param tableNamePattern The compiled table name regular expression to match against * @param snapshotNamePattern The compiled snapshot name regular expression to match against * @return returns a List of completed SnapshotDescription * @throws IOException if a remote or network exception occurs */ @Override public List listTableSnapshots(Pattern tableNamePattern, Pattern snapshotNamePattern) throws IOException { TableName[] tableNames = listTableNames(tableNamePattern); List tableSnapshots = new LinkedList(); List snapshots = listSnapshots(snapshotNamePattern); List listOfTableNames = Arrays.asList(tableNames); for (SnapshotDescription snapshot : snapshots) { if (listOfTableNames.contains(TableName.valueOf(snapshot.getTable()))) { tableSnapshots.add(snapshot); } } return tableSnapshots; } /** * Delete an existing snapshot. * @param snapshotName name of the snapshot * @throws IOException if a remote or network exception occurs */ @Override public void deleteSnapshot(final byte[] snapshotName) throws IOException { deleteSnapshot(Bytes.toString(snapshotName)); } /** * Delete an existing snapshot. * @param snapshotName name of the snapshot * @throws IOException if a remote or network exception occurs */ @Override public void deleteSnapshot(final String snapshotName) throws IOException { // make sure the snapshot is possibly valid TableName.isLegalFullyQualifiedTableName(Bytes.toBytes(snapshotName)); // do the delete executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); master.deleteSnapshot(controller, DeleteSnapshotRequest.newBuilder(). setSnapshot(SnapshotDescription.newBuilder().setName(snapshotName).build()).build() ); return null; } }); } /** * Delete existing snapshots whose names match the pattern passed. * @param regex The regular expression to match against * @throws IOException if a remote or network exception occurs */ @Override public void deleteSnapshots(final String regex) throws IOException { deleteSnapshots(Pattern.compile(regex)); } /** * Delete existing snapshots whose names match the pattern passed. * @param pattern pattern for names of the snapshot to match * @throws IOException if a remote or network exception occurs */ @Override public void deleteSnapshots(final Pattern pattern) throws IOException { List snapshots = listSnapshots(pattern); for (final SnapshotDescription snapshot : snapshots) { try { internalDeleteSnapshot(snapshot); } catch (IOException ex) { LOG.info( "Failed to delete snapshot " + snapshot.getName() + " for table " + snapshot.getTable(), ex); } } } private void internalDeleteSnapshot(final SnapshotDescription snapshot) throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); this.master.deleteSnapshot(controller, DeleteSnapshotRequest.newBuilder() .setSnapshot(snapshot).build()); return null; } }); } /** * Delete all existing snapshots matching the given table name regular expression and snapshot * name regular expression. * @param tableNameRegex The table name regular expression to match against * @param snapshotNameRegex The snapshot name regular expression to match against * @throws IOException if a remote or network exception occurs */ @Override public void deleteTableSnapshots(String tableNameRegex, String snapshotNameRegex) throws IOException { deleteTableSnapshots(Pattern.compile(tableNameRegex), Pattern.compile(snapshotNameRegex)); } /** * Delete all existing snapshots matching the given table name regular expression and snapshot * name regular expression. * @param tableNamePattern The compiled table name regular expression to match against * @param snapshotNamePattern The compiled snapshot name regular expression to match against * @throws IOException if a remote or network exception occurs */ @Override public void deleteTableSnapshots(Pattern tableNamePattern, Pattern snapshotNamePattern) throws IOException { List snapshots = listTableSnapshots(tableNamePattern, snapshotNamePattern); for (SnapshotDescription snapshot : snapshots) { try { internalDeleteSnapshot(snapshot); LOG.debug("Successfully deleted snapshot: " + snapshot.getName()); } catch (IOException e) { LOG.error("Failed to delete snapshot: " + snapshot.getName(), e); } } } /** * Apply the new quota settings. * @param quota the quota settings * @throws IOException if a remote or network exception occurs */ @Override public void setQuota(final QuotaSettings quota) throws IOException { executeCallable(new MasterCallable(getConnection()) { @Override public Void call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); this.master.setQuota(controller, QuotaSettings.buildSetQuotaRequestProto(quota)); return null; } }); } /** * Return a Quota Scanner to list the quotas based on the filter. * @param filter the quota settings filter * @return the quota scanner * @throws IOException if a remote or network exception occurs */ @Override public QuotaRetriever getQuotaRetriever(final QuotaFilter filter) throws IOException { return QuotaRetriever.open(conf, filter); } private V executeCallable(MasterCallable callable) throws IOException { return executeCallable(callable, rpcCallerFactory, operationTimeout, rpcTimeout); } private static V executeCallable(MasterCallable callable, RpcRetryingCallerFactory rpcCallerFactory, int operationTimeout, int rpcTimeout) throws IOException { RpcRetryingCaller caller = rpcCallerFactory.newCaller(rpcTimeout); try { return caller.callWithRetries(callable, operationTimeout); } finally { callable.close(); } } /** * Creates and returns a {@link com.google.protobuf.RpcChannel} instance * connected to the active master. * *

* The obtained {@link com.google.protobuf.RpcChannel} instance can be used to access a published * coprocessor {@link com.google.protobuf.Service} using standard protobuf service invocations: *

* *
*
   * CoprocessorRpcChannel channel = myAdmin.coprocessorService();
   * MyService.BlockingInterface service = MyService.newBlockingStub(channel);
   * MyCallRequest request = MyCallRequest.newBuilder()
   *     ...
   *     .build();
   * MyCallResponse response = service.myCall(null, request);
   * 
* * @return A MasterCoprocessorRpcChannel instance */ @Override public CoprocessorRpcChannel coprocessorService() { return new MasterCoprocessorRpcChannel(connection); } /** * Simple {@link Abortable}, throwing RuntimeException on abort. */ private static class ThrowableAbortable implements Abortable { @Override public void abort(String why, Throwable e) { throw new RuntimeException(why, e); } @Override public boolean isAborted() { return true; } } /** * Creates and returns a {@link com.google.protobuf.RpcChannel} instance * connected to the passed region server. * *

* The obtained {@link com.google.protobuf.RpcChannel} instance can be used to access a published * coprocessor {@link com.google.protobuf.Service} using standard protobuf service invocations: *

* *
*
   * CoprocessorRpcChannel channel = myAdmin.coprocessorService(serverName);
   * MyService.BlockingInterface service = MyService.newBlockingStub(channel);
   * MyCallRequest request = MyCallRequest.newBuilder()
   *     ...
   *     .build();
   * MyCallResponse response = service.myCall(null, request);
   * 
* * @param sn the server name to which the endpoint call is made * @return A RegionServerCoprocessorRpcChannel instance */ @Override public CoprocessorRpcChannel coprocessorService(ServerName sn) { return new RegionServerCoprocessorRpcChannel(connection, sn); } @Override public void updateConfiguration(ServerName server) throws IOException { try { this.connection.getAdmin(server).updateConfiguration(null, UpdateConfigurationRequest.getDefaultInstance()); } catch (ServiceException e) { throw ProtobufUtil.getRemoteException(e); } } @Override public void updateConfiguration() throws IOException { for (ServerName server : this.getClusterStatus().getServers()) { updateConfiguration(server); } updateConfiguration(this.getClusterStatus().getMaster()); for (ServerName server : this.getClusterStatus().getBackupMasters()) { updateConfiguration(server); } } /** * @return current master server name * @throws IOException if a remote or network exception occurs */ @Override public ServerName getMaster() throws IOException { ConnectionManager.HConnectionImplementation connection = (ConnectionManager.HConnectionImplementation)this.connection; ZooKeeperKeepAliveConnection zkw = connection.getKeepAliveZooKeeperWatcher(); try { return MasterAddressTracker.getMasterAddress(zkw); } catch (Exception e) { throw new IOException("Failed to get master address from MasterAddressTracker", e); } } @Override public int getMasterInfoPort() throws IOException { ConnectionManager.HConnectionImplementation connection = (ConnectionManager.HConnectionImplementation)this.connection; ZooKeeperKeepAliveConnection zkw = connection.getKeepAliveZooKeeperWatcher(); try { return MasterAddressTracker.getMasterInfoPort(zkw); } catch (KeeperException e) { throw new IOException("Failed to get master info port from MasterAddressTracker", e); } } @Override public long getLastMajorCompactionTimestamp(final TableName tableName) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Long call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); MajorCompactionTimestampRequest req = MajorCompactionTimestampRequest.newBuilder() .setTableName(ProtobufUtil.toProtoTableName(tableName)).build(); return master.getLastMajorCompactionTimestamp(controller, req).getCompactionTimestamp(); } }); } @Override public long getLastMajorCompactionTimestampForRegion(final byte[] regionName) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Long call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); MajorCompactionTimestampForRegionRequest req = MajorCompactionTimestampForRegionRequest .newBuilder() .setRegion( RequestConverter .buildRegionSpecifier(RegionSpecifierType.REGION_NAME, regionName)).build(); return master.getLastMajorCompactionTimestampForRegion(controller, req) .getCompactionTimestamp(); } }); } /** * Future that waits on a procedure result. * Returned by the async version of the Admin calls, * and used internally by the sync calls to wait on the result of the procedure. */ @InterfaceAudience.Private @InterfaceStability.Evolving protected static class ProcedureFuture implements Future { private ExecutionException exception = null; private boolean procResultFound = false; private boolean done = false; private boolean cancelled = false; private boolean waitForOpResult = false; private V result = null; private final HBaseAdmin admin; private final Long procId; public ProcedureFuture(final HBaseAdmin admin, final Long procId) { this.admin = admin; this.procId = procId; } public ProcedureFuture(final HBaseAdmin admin, final Long procId, final boolean waitForOpResult) { this.admin = admin; this.procId = procId; this.waitForOpResult = waitForOpResult; } @Override public boolean cancel(boolean mayInterruptIfRunning) { AbortProcedureRequest abortProcRequest = AbortProcedureRequest.newBuilder() .setProcId(procId).setMayInterruptIfRunning(mayInterruptIfRunning).build(); try { cancelled = abortProcedureResult(abortProcRequest).getIsProcedureAborted(); if (cancelled) { done = true; } } catch (IOException e) { // Cancell thrown exception for some reason. At this time, we are not sure whether // the cancell succeeds or fails. We assume that it is failed, but print out a warning // for debugging purpose. LOG.warn( "Cancelling the procedure with procId=" + procId + " throws exception " + e.getMessage(), e); cancelled = false; } return cancelled; } @Override public boolean isCancelled() { return cancelled; } protected AbortProcedureResponse abortProcedureResult( final AbortProcedureRequest request) throws IOException { return admin.executeCallable(new MasterCallable( admin.getConnection()) { @Override public AbortProcedureResponse call(int callTimeout) throws ServiceException { HBaseRpcController controller = admin.getRpcControllerFactory().newController(); controller.setCallTimeout(callTimeout); return master.abortProcedure(controller, request); } }); } @Override public V get() throws InterruptedException, ExecutionException { // TODO: should we ever spin forever? throw new UnsupportedOperationException(); } @Override public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (!done) { long deadlineTs = EnvironmentEdgeManager.currentTime() + unit.toMillis(timeout); try { try { // if the master support procedures, try to wait the result if (procId != null) { result = waitProcedureResult(procId, deadlineTs); } // if we don't have a proc result, try the compatibility wait if (!procResultFound || waitForOpResult) { result = waitOperationResult(deadlineTs); } result = postOperationResult(result, deadlineTs); done = true; } catch (IOException e) { result = postOpeartionFailure(e, deadlineTs); done = true; } } catch (IOException e) { exception = new ExecutionException(e); done = true; } } if (exception != null) { throw exception; } return result; } @Override public boolean isDone() { return done; } protected HBaseAdmin getAdmin() { return admin; } private V waitProcedureResult(long procId, long deadlineTs) throws IOException, TimeoutException, InterruptedException { GetProcedureResultRequest request = GetProcedureResultRequest.newBuilder() .setProcId(procId) .build(); int tries = 0; IOException serviceEx = null; while (EnvironmentEdgeManager.currentTime() < deadlineTs) { GetProcedureResultResponse response = null; try { // Try to fetch the result response = getProcedureResult(request); } catch (IOException e) { serviceEx = unwrapException(e); // the master may be down LOG.warn("failed to get the procedure result procId=" + procId, serviceEx); // Not much to do, if we have a DoNotRetryIOException if (serviceEx instanceof DoNotRetryIOException || serviceEx instanceof NeedUnmanagedConnectionException) { // TODO: looks like there is no way to unwrap this exception and get the proper // UnsupportedOperationException aside from looking at the message. // anyway, if we fail here we just failover to the compatibility side // and that is always a valid solution. LOG.warn("Proc-v2 is unsupported on this master: " + serviceEx.getMessage(), serviceEx); procResultFound = false; waitForOpResult = false; return null; } } // If the procedure is no longer running, we should have a result if (response != null && response.getState() != GetProcedureResultResponse.State.RUNNING) { procResultFound = response.getState() != GetProcedureResultResponse.State.NOT_FOUND; return convertResult(response); } try { Thread.sleep(getAdmin().getPauseTime(tries++)); } catch (InterruptedException e) { throw new InterruptedException( "Interrupted while waiting for the result of proc " + procId); } } if (serviceEx != null) { throw serviceEx; } else { throw new TimeoutException("The procedure " + procId + " is still running"); } } private static IOException unwrapException(IOException e) { if (e instanceof RemoteException) { return ((RemoteException)e).unwrapRemoteException(); } return e; } protected GetProcedureResultResponse getProcedureResult(final GetProcedureResultRequest request) throws IOException { return admin.executeCallable(new MasterCallable( admin.getConnection()) { @Override public GetProcedureResultResponse call(int callTimeout) throws ServiceException { return master.getProcedureResult(null, request); } }); } /** * Convert the procedure result response to a specified type. * @param response the procedure result object to parse * @return the result data of the procedure. */ protected V convertResult(final GetProcedureResultResponse response) throws IOException { if (response.hasException()) { throw ForeignExceptionUtil.toIOException(response.getException()); } return null; } /** * Fallback implementation in case the procedure is not supported by the server. * It should try to wait until the operation is completed. * @param deadlineTs the timestamp after which this method should throw a TimeoutException * @return the result data of the operation */ protected V waitOperationResult(final long deadlineTs) throws IOException, TimeoutException { return null; } /** * Called after the operation is completed and the result fetched. * this allows to perform extra steps after the procedure is completed. * it allows to apply transformations to the result that will be returned by get(). * @param result the result of the procedure * @param deadlineTs the timestamp after which this method should throw a TimeoutException * @return the result of the procedure, which may be the same as the passed one */ protected V postOperationResult(final V result, final long deadlineTs) throws IOException, TimeoutException { return result; } /** * Called after the operation is terminated with a failure. * this allows to perform extra steps after the procedure is terminated. * it allows to apply transformations to the result that will be returned by get(). * The default implementation will rethrow the exception * @param exception the exception got from fetching the result * @param deadlineTs the timestamp after which this method should throw a TimeoutException * @return the result of the procedure, which may be the same as the passed one */ protected V postOpeartionFailure(final IOException exception, final long deadlineTs) throws IOException, TimeoutException { throw exception; } protected interface WaitForStateCallable { boolean checkState(int tries) throws IOException; void throwInterruptedException() throws InterruptedIOException; void throwTimeoutException(long elapsed) throws TimeoutException; } protected void waitForState(final long deadlineTs, final WaitForStateCallable callable) throws IOException, TimeoutException { int tries = 0; IOException serverEx = null; long startTime = EnvironmentEdgeManager.currentTime(); while (EnvironmentEdgeManager.currentTime() < deadlineTs) { serverEx = null; try { if (callable.checkState(tries)) { return; } } catch (IOException e) { serverEx = e; } try { Thread.sleep(getAdmin().getPauseTime(tries++)); } catch (InterruptedException e) { callable.throwInterruptedException(); } } if (serverEx != null) { throw unwrapException(serverEx); } else { callable.throwTimeoutException(EnvironmentEdgeManager.currentTime() - startTime); } } } @Override public List getSecurityCapabilities() throws IOException { try { return executeCallable(new MasterCallable>(getConnection()) { @Override public List call(int callTimeout) throws ServiceException { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); SecurityCapabilitiesRequest req = SecurityCapabilitiesRequest.newBuilder().build(); return ProtobufUtil.toSecurityCapabilityList( master.getSecurityCapabilities(controller, req).getCapabilitiesList()); } }); } catch (IOException e) { if (e instanceof RemoteException) { e = ((RemoteException)e).unwrapRemoteException(); } throw e; } } @Override public boolean[] setSplitOrMergeEnabled(final boolean enabled, final boolean synchronous, final MasterSwitchType... switchTypes) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public boolean[] call(int callTimeout) throws ServiceException { MasterProtos.SetSplitOrMergeEnabledResponse response = master.setSplitOrMergeEnabled(null, RequestConverter.buildSetSplitOrMergeEnabledRequest(enabled, synchronous, switchTypes)); boolean[] result = new boolean[switchTypes.length]; int i = 0; for (Boolean prevValue : response.getPrevValueList()) { result[i++] = prevValue; } return result; } }); } @Override public boolean isSplitOrMergeEnabled(final MasterSwitchType switchType) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws ServiceException { return master.isSplitOrMergeEnabled(null, RequestConverter.buildIsSplitOrMergeEnabledRequest(switchType)).getEnabled(); } }); } @Override public List listDeadServers() throws IOException { return new ArrayList<>(getClusterStatus().getDeadServerNames()); } @Override public List clearDeadServers(final List servers) throws IOException { if (servers == null || servers.size() == 0) { throw new IllegalArgumentException("servers cannot be null or empty"); } return executeCallable(new MasterCallable>(getConnection()) { @Override public List call(int callTimeout) throws Exception { ClearDeadServersRequest req = RequestConverter.buildClearDeadServersRequest(servers); return ProtobufUtil.toServerNameList( master.clearDeadServers(null, req).getServerNameList()); } }); } private RpcControllerFactory getRpcControllerFactory() { return rpcControllerFactory; } @Override public boolean snapshotCleanupSwitch(final boolean on, final boolean synchronous) throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); SetSnapshotCleanupRequest req = RequestConverter.buildSetSnapshotCleanupRequest(on, synchronous); return master.switchSnapshotCleanup(controller, req).getPrevSnapshotCleanup(); } }); } @Override public boolean isSnapshotCleanupEnabled() throws IOException { return executeCallable(new MasterCallable(getConnection()) { @Override public Boolean call(int callTimeout) throws Exception { HBaseRpcController controller = rpcControllerFactory.newController(); controller.setCallTimeout(callTimeout); IsSnapshotCleanupEnabledRequest req = RequestConverter.buildIsSnapshotCleanupEnabledRequest(); return master.isSnapshotCleanupEnabled(controller, req).getEnabled(); } }); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy