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

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

Go to download

This project contains artifacts that adapt bigtable client to work with hbase.

There is a newer version: 2.14.8
Show newest version
/*
 * Copyright 2015 Google LLC
 *
 * Licensed 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 static com.google.cloud.bigtable.hbase.util.ModifyTableBuilder.buildModifications;

import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutureCallback;
import com.google.api.core.ApiFutures;
import com.google.api.core.InternalApi;
import com.google.api.gax.rpc.FailedPreconditionException;
import com.google.cloud.bigtable.admin.v2.models.Backup;
import com.google.cloud.bigtable.admin.v2.models.Cluster;
import com.google.cloud.bigtable.admin.v2.models.CreateBackupRequest;
import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest;
import com.google.cloud.bigtable.admin.v2.models.ModifyColumnFamiliesRequest;
import com.google.cloud.bigtable.admin.v2.models.RestoreTableRequest;
import com.google.cloud.bigtable.admin.v2.models.Table;
import com.google.cloud.bigtable.hbase.BigtableOptionsFactory;
import com.google.cloud.bigtable.hbase.adapters.admin.TableAdapter;
import com.google.cloud.bigtable.hbase.util.FutureUtil;
import com.google.cloud.bigtable.hbase.util.Logger;
import com.google.cloud.bigtable.hbase.util.ModifyTableBuilder;
import com.google.cloud.bigtable.hbase.wrappers.AdminClientWrapper;
import com.google.cloud.bigtable.hbase.wrappers.BigtableHBaseSettings;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import com.google.protobuf.ByteString;
import io.grpc.Status;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseIOException;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableExistsException;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNotEnabledException;
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
import org.apache.hadoop.hbase.snapshot.SnapshotCreationException;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;
import org.threeten.bp.Instant;
import org.threeten.bp.temporal.ChronoUnit;

/**
 * Abstract AbstractBigtableAdmin class.
 *
 * 

For internal use only - public for technical reasons. */ @InternalApi("For internal usage only") @SuppressWarnings("deprecation") public abstract class AbstractBigtableAdmin implements Admin { protected final Logger LOG = new Logger(getClass()); /** * Bigtable doesn't require disabling tables before deletes or schema changes. Some clients do * call disable first, and then check for disable before deletes or schema changes. We're keeping * track of that state in memory on so that those clients can proceed with the delete/schema * change */ private final Set disabledTables; private final Configuration configuration; private final BigtableHBaseSettings settings; protected final CommonConnection connection; protected final AdminClientWrapper adminClientWrapper; private String bigtableSnapshotClusterId; /** * Constructor for AbstractBigtableAdmin. * * @param connection a {@link CommonConnection} object. * @throws IOException if any. */ public AbstractBigtableAdmin(CommonConnection connection) throws IOException { LOG.debug("Creating BigtableAdmin"); configuration = connection.getConfiguration(); settings = connection.getBigtableSettings(); this.connection = connection; disabledTables = connection.getDisabledTables(); adminClientWrapper = connection.getBigtableApi().getAdminClient(); String clusterId = configuration.get(BigtableOptionsFactory.BIGTABLE_SNAPSHOT_CLUSTER_ID_KEY, null); if (clusterId != null) { bigtableSnapshotClusterId = clusterId; } } /** {@inheritDoc} */ @Override public Connection getConnection() { return (Connection) connection; } /** {@inheritDoc} */ @Override public boolean tableExists(TableName tableName) throws IOException { for (TableName existingTableName : listTableNames(tableName.getNameAsString())) { if (existingTableName.equals(tableName)) { return true; } } return false; } // Used by the Hbase shell but not defined by Admin. Will be removed once the // shell is switch to use the methods defined in the interface. /** * tableExists. * * @param tableName a {@link java.lang.String} object. * @return a boolean. * @throws java.io.IOException if any. */ @Deprecated public boolean tableExists(String tableName) throws IOException { return tableExists(TableName.valueOf(tableName)); } /** {@inheritDoc} */ @Override public HTableDescriptor[] listTables() throws IOException { // NOTE: We don't have systables. return getTableDescriptorsIgnoreFailure(listTableNames()); } private HTableDescriptor[] getTableDescriptors(TableName[] tableNames) throws IOException { HTableDescriptor[] response = new HTableDescriptor[tableNames.length]; for (int i = 0; i < tableNames.length; i++) { response[i] = getTableDescriptor(tableNames[i]); } return response; } // BigtableTableAdmin#listTables doesn't include table details (like column families), so the // table descriptors must be fetched into 2 phases: // 1. list all of the names // 2. fetch the table details // // Due to the non-atomic listing, tables in the list might disappear before fetching the details. // This method will suppress those tables. private HTableDescriptor[] getTableDescriptorsIgnoreFailure(TableName[] tableNames) throws IOException { List descriptors = new ArrayList<>(); for (TableName tableName : tableNames) { try { descriptors.add(getTableDescriptor(tableName)); } catch (IOException ex) { // This suppresses TableNotFoundException, which is for consistency with HBase layers, // and FailedPreconditionException or Status.Code.FAILED_PRECONDITION, which comes from // Bigtable table creation internal state. Both of these can occur due to race condition. if (ex instanceof TableNotFoundException || ex.getCause() instanceof FailedPreconditionException || Status.Code.FAILED_PRECONDITION == Status.fromThrowable(ex.getCause()).getCode()) { continue; } throw ex; } } return descriptors.toArray(new HTableDescriptor[0]); } /** {@inheritDoc} */ @Override public HTableDescriptor[] listTables(Pattern pattern) throws IOException { // NOTE: We don't have systables. return getTableDescriptorsIgnoreFailure(listTableNames(pattern)); } /** {@inheritDoc} */ @Override public HTableDescriptor[] listTables(final Pattern pattern, final boolean includeSysTables) throws IOException { return listTables(pattern); } // Used by the Hbase shell but not defined by Admin. Will be removed once the // shell is switch to use the methods defined in the interface. /** {@inheritDoc} */ @Override @Deprecated public TableName[] listTableNames(String patternStr) throws IOException { return listTableNames(Pattern.compile(patternStr)); } /** {@inheritDoc} */ @Override public TableName[] listTableNames(Pattern pattern) throws IOException { if (pattern == null) { return listTableNames(); } List result = new ArrayList<>(); for (TableName tableName : listTableNames()) { if (pattern.matcher(tableName.getNameAsString()).matches()) { result.add(tableName); } } return result.toArray(new TableName[result.size()]); } /** {@inheritDoc} */ @Override public TableName[] listTableNames(Pattern pattern, boolean includeSysTables) throws IOException { return listTableNames(pattern); } /** {@inheritDoc} */ @Override public TableName[] listTableNames(String regex, boolean includeSysTables) throws IOException { return listTableNames(regex); } /** {@inheritDoc} */ @Override public HTableDescriptor[] listTables(String regex) throws IOException { return listTables(Pattern.compile(regex)); } /** {@inheritDoc} */ @Override public HTableDescriptor[] listTables(String regex, boolean includeSysTables) throws IOException { return listTables(regex); } /** Lists all table names for the cluster provided in the configuration. */ @Override public TableName[] listTableNames() throws IOException { // tablesList contains list of tableId. List tablesList = FutureUtil.unwrap(adminClientWrapper.listTablesAsync()); TableName[] result = new TableName[tablesList.size()]; for (int i = 0; i < tablesList.size(); i++) { result[i] = TableName.valueOf(tablesList.get(i)); } return result; } /** {@inheritDoc} */ @Override public HTableDescriptor getTableDescriptor(TableName tableName) throws IOException { if (tableName == null) { return null; } try { return TableAdapter.adapt( FutureUtil.unwrap(adminClientWrapper.getTableAsync(tableName.getNameAsString()))); } catch (Throwable throwable) { if (Status.fromThrowable(throwable).getCode() == Status.Code.NOT_FOUND) { throw new TableNotFoundException(tableName); } throw new IOException("Failed to getTableDescriptor() on " + tableName, throwable); } } // Used by the Hbase shell but not defined by Admin. Will be removed once the // shell is switch to use the methods defined in the interface. /** * getTableNames. * * @param regex a {@link String} object. * @return an array of {@link String} objects. * @throws IOException if any. */ @Deprecated public String[] getTableNames(String regex) throws IOException { TableName[] tableNames = listTableNames(regex); String[] tableIds = new String[tableNames.length]; for (int i = 0; i < tableNames.length; i++) { tableIds[i] = tableNames[i].getNameAsString(); } return tableIds; } /** {@inheritDoc} */ @Override public void createTable(HTableDescriptor desc) throws IOException { createTable(desc, null); } /** {@inheritDoc} */ @Override public void createTable(HTableDescriptor desc, byte[] startKey, byte[] endKey, int numRegions) throws IOException { createTable(desc, createSplitKeys(startKey, endKey, numRegions)); } public static byte[][] createSplitKeys(byte[] startKey, byte[] endKey, int numRegions) { if (numRegions < 3) { throw new IllegalArgumentException("Must create at least three regions"); } else if (Bytes.compareTo(startKey, endKey) >= 0) { throw new IllegalArgumentException("Start key must be smaller than end key"); } byte[][] splitKeys; if (numRegions == 3) { splitKeys = new byte[][] {startKey, endKey}; } else { splitKeys = Bytes.split(startKey, endKey, numRegions - 3); if (splitKeys == null || splitKeys.length != numRegions - 1) { throw new IllegalArgumentException("Unable to split key range into enough regions"); } } return splitKeys; } /** {@inheritDoc} */ @Override public void createTable(HTableDescriptor desc, byte[][] splitKeys) throws IOException { createTable(desc.getTableName(), TableAdapter.adapt(desc, splitKeys)); } /** * Creates a Table. * * @param tableName a {@link TableName} object. * @param request a {@link CreateTableRequest} object to send. * @throws java.io.IOException if any. */ protected void createTable(TableName tableName, CreateTableRequest request) throws IOException { try { FutureUtil.unwrap(adminClientWrapper.createTableAsync(request)); } catch (Throwable throwable) { throw convertToTableExistsException(tableName, throwable); } } /** {@inheritDoc} */ @Override public void createTableAsync(final HTableDescriptor desc, byte[][] splitKeys) throws IOException { LOG.warn("Creating the table synchronously"); createTableAsync(desc.getTableName(), TableAdapter.adapt(desc, splitKeys)); } /** * @param tableName a {@link TableName} object for exception identification. * @param request a {@link CreateTableRequest} object to send. * @return a {@link ListenableFuture} object. * @throws java.io.IOException if any. */ protected ListenableFuture

createTableAsync( final TableName tableName, CreateTableRequest request) throws IOException { ApiFuture
future = adminClientWrapper.createTableAsync(request); final SettableFuture
settableFuture = SettableFuture.create(); ApiFutures.addCallback( future, new ApiFutureCallback
() { @Override public void onSuccess(@Nullable Table result) { settableFuture.set(result); } @Override public void onFailure(Throwable t) { settableFuture.setException(convertToTableExistsException(tableName, t)); } }, MoreExecutors.directExecutor()); return settableFuture; } public static IOException convertToTableExistsException( TableName tableName, Throwable throwable) { if (Status.fromThrowable(throwable).getCode() == Status.Code.ALREADY_EXISTS) { return new TableExistsException(tableName); } else { return new IOException(String.format("Failed to create table '%s'", tableName), throwable); } } /** {@inheritDoc} */ @Override public void deleteTable(TableName tableName) throws IOException { try { FutureUtil.unwrap(adminClientWrapper.deleteTableAsync(tableName.getNameAsString())); } catch (Throwable throwable) { throw new IOException( String.format("Failed to delete table '%s'", tableName.getNameAsString()), throwable); } disabledTables.remove(tableName); } /** {@inheritDoc} */ @Override public HTableDescriptor[] deleteTables(String regex) throws IOException { return deleteTables(Pattern.compile(regex)); } /** {@inheritDoc} */ @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()]); } /** {@inheritDoc} */ @Override public void enableTable(TableName tableName) throws IOException { TableName.isLegalFullyQualifiedTableName(tableName.getName()); if (!tableExists(tableName)) { throw new TableNotFoundException(tableName); } disabledTables.remove(tableName); LOG.warn("Table " + tableName + " was enabled in memory only."); } // Used by the Hbase shell but not defined by Admin. Will be removed once the // shell is switch to use the methods defined in the interface. /** * enableTable. * * @param tableName a {@link java.lang.String} object. * @throws java.io.IOException if any. */ @Deprecated public void enableTable(String tableName) throws IOException { enableTable(TableName.valueOf(tableName)); } /** {@inheritDoc} */ @Override public HTableDescriptor[] enableTables(String regex) throws IOException { HTableDescriptor[] tableDescriptors = listTables(regex); for (HTableDescriptor descriptor : tableDescriptors) { enableTable(descriptor.getTableName()); } return tableDescriptors; } /** {@inheritDoc} */ @Override public HTableDescriptor[] enableTables(Pattern pattern) throws IOException { HTableDescriptor[] tableDescriptors = listTables(pattern); for (HTableDescriptor descriptor : tableDescriptors) { enableTable(descriptor.getTableName()); } return tableDescriptors; } /** {@inheritDoc} */ @Override public void disableTable(TableName tableName) throws IOException { TableName.isLegalFullyQualifiedTableName(tableName.getName()); if (!tableExists(tableName)) { throw new TableNotFoundException(tableName); } if (isTableDisabled(tableName)) { throw new TableNotEnabledException(tableName); } disabledTables.add(tableName); LOG.warn("Table " + tableName + " was disabled in memory only."); } // Used by the Hbase shell but not defined by Admin. Will be removed once the // shell is switch to use the methods defined in the interface. /** * disableTable. * * @param tableName a {@link java.lang.String} object. * @throws java.io.IOException if any. */ @Deprecated public void disableTable(String tableName) throws IOException { disableTable(TableName.valueOf(tableName)); } /** {@inheritDoc} */ @Override public HTableDescriptor[] disableTables(String regex) throws IOException { HTableDescriptor[] tableDescriptors = listTables(regex); for (HTableDescriptor descriptor : tableDescriptors) { disableTable(descriptor.getTableName()); } return tableDescriptors; } /** {@inheritDoc} */ @Override public HTableDescriptor[] disableTables(Pattern pattern) throws IOException { HTableDescriptor[] tableDescriptors = listTables(pattern); for (HTableDescriptor descriptor : tableDescriptors) { disableTable(descriptor.getTableName()); } return tableDescriptors; } /** {@inheritDoc} */ @Override public boolean isTableEnabled(TableName tableName) throws IOException { return !isTableDisabled(tableName); } // Used by the Hbase shell but not defined by Admin. Will be removed once the // shell is switch to use the methods defined in the interface. /** * isTableEnabled. * * @param tableName a {@link java.lang.String} object. * @return a boolean. * @throws java.io.IOException if any. */ @Deprecated public boolean isTableEnabled(String tableName) throws IOException { return isTableEnabled(TableName.valueOf(tableName)); } /** {@inheritDoc} */ @Override public boolean isTableDisabled(TableName tableName) throws IOException { Preconditions.checkNotNull(tableName, "TableName cannot be null"); return disabledTables.contains(tableName); } // Used by the Hbase shell but not defined by Admin. Will be removed once the // shell is switch to use the methods defined in the interface. /** * isTableDisabled. * * @param tableName a {@link java.lang.String} object. * @return a boolean. * @throws java.io.IOException if any. */ @Deprecated public boolean isTableDisabled(String tableName) throws IOException { return isTableDisabled(TableName.valueOf(tableName)); } /** {@inheritDoc} */ @Override public boolean isTableAvailable(TableName tableName) throws IOException { return tableExists(tableName); } /** {@inheritDoc} */ @Override public void addColumn(TableName tableName, HColumnDescriptor column) throws IOException { modifyColumns( tableName, column.getNameAsString(), "add", ModifyTableBuilder.newBuilder(tableName).add(column)); } /** {@inheritDoc} */ @Override public void modifyColumn(TableName tableName, HColumnDescriptor column) throws IOException { modifyColumns( tableName, column.getNameAsString(), "modify", ModifyTableBuilder.newBuilder(tableName).modify(column)); } /** {@inheritDoc} */ @Override public void deleteColumn(TableName tableName, byte[] columnName) throws IOException { String name = Bytes.toString(columnName); modifyColumns(tableName, name, "delete", ModifyTableBuilder.newBuilder(tableName).delete(name)); } /** {@inheritDoc} */ @Override public void modifyTable(TableName tableName, HTableDescriptor newDescriptor) throws IOException { if (isTableAvailable(tableName)) { try { ModifyColumnFamiliesRequest request = buildModifications(newDescriptor, getTableDescriptor(tableName)).build(); FutureUtil.unwrap(adminClientWrapper.modifyFamiliesAsync(request)); } catch (Throwable throwable) { throw new IOException( String.format("Failed to modify table '%s'", tableName.getNameAsString()), throwable); } } else { throw new TableNotFoundException(tableName); } } /** * modifyColumns. * * @param tableName a {@link TableName} object for error messages. * @param columnName a {@link String} object for error messages * @param modificationType a {@link String} object for error messages * @param builder a {@link ModifyTableBuilder} object to send. * @throws java.io.IOException if any. */ protected Void modifyColumns( TableName tableName, String columnName, String modificationType, ModifyTableBuilder builder) throws IOException { try { FutureUtil.unwrap(adminClientWrapper.modifyFamiliesAsync(builder.build())); return null; } catch (Throwable throwable) { throw new IOException( String.format( "Failed to %s column '%s' in table '%s'", modificationType, columnName, tableName.getNameAsString()), throwable); } } // Used by the Hbase shell but not defined by Admin. Will be removed once the // shell is switch to use the methods defined in the interface. @Deprecated public void addColumn(String tableName, HColumnDescriptor column) throws IOException { addColumn(TableName.valueOf(tableName), column); } /** * Modify an existing column family on a table. NOTE: this is needed for backwards compatibility * for the hbase shell. * * @param tableName a {@link TableName} object. * @param descriptor a {@link HColumnDescriptor} object. * @throws java.io.IOException if any. */ public void modifyColumns(final String tableName, HColumnDescriptor descriptor) throws IOException { modifyColumn(TableName.valueOf(tableName), descriptor); } // Used by the Hbase shell but not defined by Admin. Will be removed once the // shell is switch to use the methods defined in the interface. /** * deleteColumn. * * @param tableName a {@link java.lang.String} object. * @param columnName an array of byte. * @throws java.io.IOException if any. */ @Deprecated public void deleteColumn(String tableName, byte[] columnName) throws IOException { deleteColumn(TableName.valueOf(tableName), columnName); } // Used by the Hbase shell but not defined by Admin. Will be removed once the // shell is switch to use the methods defined in the interface. /** * deleteColumn. * * @param tableName a {@link java.lang.String} object. * @param columnName a {@link java.lang.String} object. * @throws java.io.IOException if any. */ @Deprecated public void deleteColumn(final String tableName, final String columnName) throws IOException { deleteColumn(TableName.valueOf(tableName), Bytes.toBytes(columnName)); } /** {@inheritDoc} */ @Override public Configuration getConfiguration() { return configuration; } /** {@inheritDoc} */ @Override public List getTableRegions(TableName tableName) throws IOException { return connection.getAllRegionInfos(tableName); } /** {@inheritDoc} */ @Override public void close() throws IOException { // no-op } /** {@inheritDoc} */ @Override public HTableDescriptor[] getTableDescriptorsByTableName(List tableNames) throws IOException { if (tableNames == null || tableNames.isEmpty()) { return listTables(); } TableName[] tableNameArray = tableNames.toArray(new TableName[tableNames.size()]); return getTableDescriptors(tableNameArray); } /** {@inheritDoc} */ @Override public HTableDescriptor[] getTableDescriptors(List names) throws IOException { Preconditions.checkNotNull(names); if (names.isEmpty()) { return listTables(); } TableName[] tableNameArray = new TableName[names.size()]; for (int i = 0; i < names.size(); i++) { tableNameArray[i] = TableName.valueOf(names.get(i)); } return getTableDescriptors(tableNameArray); } /** {@inheritDoc} */ @Override public String toString() { return MoreObjects.toStringHelper(getClass()) .add("project", settings.getProjectId()) .add("instance", settings.getInstanceId()) .add("adminHost", settings.getAdminHost()) .toString(); } /* Unsupported operations */ /** {@inheritDoc} */ @Override public int getOperationTimeout() { throw new UnsupportedOperationException("getOperationTimeout"); // TODO } /** {@inheritDoc} */ @Override public void abort(String why, Throwable e) { throw new UnsupportedOperationException("abort"); // TODO } /** {@inheritDoc} */ @Override public boolean isAborted() { throw new UnsupportedOperationException("isAborted"); // TODO } /** {@inheritDoc} */ @Override public void truncateTable(TableName tableName, boolean preserveSplits) throws IOException { if (!preserveSplits) { LOG.info("truncate will preserveSplits. The passed in variable is ignored."); } try { FutureUtil.unwrap(adminClientWrapper.dropAllRowsAsync(tableName.getNameAsString())); } catch (Throwable throwable) { throw new IOException( String.format("Failed to truncate table '%s'", tableName.getNameAsString()), throwable); } disabledTables.remove(tableName); } /** * deleteRowRangeByPrefix. * * @param tableName a {@link org.apache.hadoop.hbase.TableName} object. * @param prefix an array of byte. * @throws java.io.IOException if any. */ public void deleteRowRangeByPrefix(TableName tableName, byte[] prefix) throws IOException { try { FutureUtil.unwrap( adminClientWrapper.dropRowRangeAsync( tableName.getNameAsString(), ByteString.copyFrom(prefix))); } catch (Throwable throwable) { throw new IOException( String.format("Failed to truncate table '%s'", tableName.getNameAsString()), throwable); } } /** {@inheritDoc} */ @Override public boolean isTableAvailable(TableName tableName, byte[][] splitKeys) throws IOException { return tableExists(tableName); } /** * {@inheritDoc} * *

HBase column operations are not synchronous, since they're not as fast as Bigtable. Bigtable * does not have async operations, so always return (0, 0). This is needed for some shell * operations. */ @Override public Pair getAlterStatus(TableName tableName) throws IOException { return new Pair<>(0, 0); } /** {@inheritDoc} */ @Override public Pair getAlterStatus(byte[] tableName) throws IOException { return getAlterStatus(TableName.valueOf(tableName)); } /** * getAlterStatus. * * @param tableName a {@link java.lang.String} object. * @return a {@link org.apache.hadoop.hbase.util.Pair} object. * @throws java.io.IOException if any. */ public Pair getAlterStatus(String tableName) throws IOException { return getAlterStatus(TableName.valueOf(tableName)); } // ------------ SNAPSHOT methods begin /** * This is needed for the hbase shell. * * @param snapshotId a byte array object. * @param tableName a byte array object. * @throws IOException if any. */ public void snapshot(byte[] snapshotId, byte[] tableName) throws IOException, IllegalArgumentException { snapshot(snapshotId, TableName.valueOf(tableName)); } /** * Creates a snapshot from an existing table. NOTE: Cloud Bigtable has a cleanup policy * * @param snapshotId a {@link String} object. * @param tableName a {@link TableName} object. * @throws IOException if any. */ @Override public void snapshot(String snapshotId, TableName tableName) throws IOException, SnapshotCreationException, IllegalArgumentException { snapshotTable(snapshotId, tableName); } /** {@inheritDoc} */ @Override public void snapshot(byte[] snapshotId, TableName tableName) throws IOException, IllegalArgumentException { snapshot(Bytes.toString(snapshotId), tableName); } /** This is needed for the hbase shell. */ public void cloneSnapshot(byte[] snapshotId, byte[] tableName) throws IOException { cloneSnapshot(snapshotId, TableName.valueOf(tableName)); } /** * @param snapshotName a {@link String} object. * @param tableName a {@link TableName} object. * @throws IOException if any. */ @Override public void cloneSnapshot(byte[] snapshotName, TableName tableName) throws IOException, TableExistsException, RestoreSnapshotException { cloneSnapshot(Bytes.toString(snapshotName), tableName); } /** * @param snapshotId a {@link String} object. * @param tableName a {@link TableName} object. * @throws IOException if any. */ @Override public void cloneSnapshot(String snapshotId, TableName tableName) throws IOException, TableExistsException, RestoreSnapshotException { RestoreTableRequest request = RestoreTableRequest.of(getBackupClusterId(), snapshotId) .setTableId(tableName.getNameAsString()); FutureUtil.unwrap(adminClientWrapper.restoreTableAsync(request)); } /** {@inheritDoc} */ @Override public void deleteSnapshot(byte[] snapshotId) throws IOException { deleteSnapshot(Bytes.toString(snapshotId)); } /** {@inheritDoc} */ @Override public void deleteSnapshot(String snapshotId) throws IOException { Preconditions.checkNotNull(snapshotId); if (snapshotId.isEmpty()) { return; } FutureUtil.unwrap(adminClientWrapper.deleteBackupAsync(getBackupClusterId(), snapshotId)); } protected Backup snapshotTable(String snapshotId, TableName tableName) throws IOException { Preconditions.checkArgument(!Strings.isNullOrEmpty(snapshotId)); Preconditions.checkArgument(!Strings.isNullOrEmpty(tableName.getNameAsString())); CreateBackupRequest request = CreateBackupRequest.of(getBackupClusterId(), snapshotId) .setSourceTableId(tableName.getNameAsString()); int ttlSecondsForBackup = settings.getTtlSecondsForBackup(); if (ttlSecondsForBackup <= 0) { throw new IllegalArgumentException( BigtableOptionsFactory.BIGTABLE_SNAPSHOT_DEFAULT_TTL_SECS_KEY + " must be > 0"); } Instant expireTime = Instant.now().plus(ttlSecondsForBackup, ChronoUnit.SECONDS); request.setExpireTime(expireTime); return FutureUtil.unwrap(adminClientWrapper.createBackupAsync(request)); } @Override public void deleteSnapshots(String regex) throws IOException { throw new UnsupportedOperationException("use deleteSnapshot instead"); } @Override public void deleteSnapshots(Pattern pattern) throws IOException { throw new UnsupportedOperationException("use deleteSnapshot instead"); } @Override public void deleteTableSnapshots(String tableNameRegex, String snapshotNameRegex) throws IOException { throw new UnsupportedOperationException("Unsupported - please use deleteSnapshots"); } @Override public void deleteTableSnapshots(Pattern tableNamePattern, Pattern snapshotNamePattern) throws IOException { throw new UnsupportedOperationException("Unsupported - please use deleteSnapshots"); } @Override public void restoreSnapshot(byte[] snapshotName) throws IOException, RestoreSnapshotException { throw new UnsupportedOperationException("restoreSnapshot"); // TODO } @Override public void restoreSnapshot(String snapshotName) throws IOException, RestoreSnapshotException { throw new UnsupportedOperationException("restoreSnapshot"); // TODO } @Override public void restoreSnapshot(byte[] snapshotName, boolean takeFailSafeSnapshot) throws IOException, RestoreSnapshotException { throw new UnsupportedOperationException("restoreSnapshot"); // TODO } @Override public void restoreSnapshot(String snapshotName, boolean takeFailSafeSnapshot) throws IOException, RestoreSnapshotException { throw new UnsupportedOperationException("restoreSnapshot"); // TODO } protected synchronized String getBackupClusterId() { if (this.bigtableSnapshotClusterId == null) { List clusters = adminClientWrapper.listClusters(settings.getInstanceId()); Preconditions.checkState( clusters.size() == 1, String.format( "Project '%s' / Instance '%s' has %d clusters. There must be exactly 1 for this operation to work.", settings.getProjectId(), settings.getInstanceId(), clusters.size())); bigtableSnapshotClusterId = clusters.get(0).getId(); } return bigtableSnapshotClusterId; } /** {@inheritDoc} */ @Override public void closeRegion(String regionname, String serverName) throws IOException { throw new UnsupportedOperationException("closeRegion"); // TODO } /** {@inheritDoc} */ @Override public void closeRegion(byte[] regionname, String serverName) throws IOException { throw new UnsupportedOperationException("closeRegion"); // TODO } /** {@inheritDoc} */ @Override public boolean closeRegionWithEncodedRegionName(String encodedRegionName, String serverName) throws IOException { throw new UnsupportedOperationException("closeRegionWithEncodedRegionName"); // TODO } /** {@inheritDoc} */ @Override public void closeRegion(ServerName sn, HRegionInfo hri) throws IOException { throw new UnsupportedOperationException("closeRegion"); // TODO } /** {@inheritDoc} */ @Override public List getOnlineRegions(ServerName sn) throws IOException { throw new UnsupportedOperationException("getOnlineRegions"); // TODO } /** {@inheritDoc} */ @Override public void flush(TableName tableName) throws IOException { throw new UnsupportedOperationException("flush"); // TODO } /** {@inheritDoc} */ @Override public void flushRegion(byte[] bytes) throws IOException { LOG.info("flushRegion is a no-op"); } /** {@inheritDoc} */ @Override public void compact(TableName tableName) throws IOException { LOG.info("compact is a no-op"); } /** {@inheritDoc} */ @Override public void compactRegion(byte[] bytes) throws IOException { LOG.info("compactRegion is a no-op"); } /** {@inheritDoc} */ @Override public void compact(TableName tableName, byte[] bytes) throws IOException { LOG.info("compact is a no-op"); } /** {@inheritDoc} */ @Override public void compactRegion(byte[] bytes, byte[] bytes2) throws IOException { LOG.info("compactRegion is a no-op"); } /** {@inheritDoc} */ @Override public void majorCompact(TableName tableName) throws IOException { LOG.info("majorCompact is a no-op"); } /** {@inheritDoc} */ @Override public void majorCompactRegion(byte[] bytes) throws IOException { LOG.info("majorCompactRegion is a no-op"); } /** {@inheritDoc} */ @Override public void majorCompact(TableName tableName, byte[] bytes) throws IOException { LOG.info("majorCompact is a no-op"); } /** {@inheritDoc} */ @Override public void majorCompactRegion(byte[] bytes, byte[] bytes2) throws IOException { LOG.info("majorCompactRegion is a no-op"); } /** {@inheritDoc} */ @Override public void compactRegionServer(ServerName serverName, boolean b) throws IOException { LOG.info("compactRegionServer is a no-op"); } /** {@inheritDoc} */ @Override public void move(byte[] encodedRegionName, byte[] destServerName) throws HBaseIOException, MasterNotRunningException, ZooKeeperConnectionException { LOG.info("move is a no-op"); } /** {@inheritDoc} */ @Override public void assign(byte[] regionName) throws MasterNotRunningException, ZooKeeperConnectionException, IOException { LOG.info("assign is a no-op"); } /** {@inheritDoc} */ @Override public void unassign(byte[] regionName, boolean force) throws MasterNotRunningException, ZooKeeperConnectionException, IOException { LOG.info("unassign is a no-op"); } /** {@inheritDoc} */ @Override public void offline(byte[] regionName) throws IOException { throw new UnsupportedOperationException("offline"); // TODO } /** {@inheritDoc} */ @Override public boolean setBalancerRunning(boolean on, boolean synchronous) throws MasterNotRunningException, ZooKeeperConnectionException { throw new UnsupportedOperationException("setBalancerRunning"); // TODO } /** {@inheritDoc} */ @Override public boolean balancer() throws MasterNotRunningException, ZooKeeperConnectionException { throw new UnsupportedOperationException("balancer"); // TODO } /** {@inheritDoc} */ @Override public boolean enableCatalogJanitor(boolean enable) throws MasterNotRunningException { throw new UnsupportedOperationException("enableCatalogJanitor"); // TODO } /** {@inheritDoc} */ @Override public int runCatalogScan() throws MasterNotRunningException { throw new UnsupportedOperationException("runCatalogScan"); // TODO } /** {@inheritDoc} */ @Override public boolean isCatalogJanitorEnabled() throws MasterNotRunningException { throw new UnsupportedOperationException("isCatalogJanitorEnabled"); // TODO } /** {@inheritDoc} */ @Override public void mergeRegions( byte[] encodedNameOfRegionA, byte[] encodedNameOfRegionB, boolean forcible) throws IOException { LOG.info("mergeRegions is a no-op"); } /** {@inheritDoc} */ @Override public void split(TableName tableName) throws IOException { LOG.info("split is a no-op"); } /** {@inheritDoc} */ @Override public void splitRegion(byte[] bytes) throws IOException { LOG.info("splitRegion is a no-op"); } /** {@inheritDoc} */ @Override public void split(TableName tableName, byte[] bytes) throws IOException { LOG.info("split is a no-op"); } /** {@inheritDoc} */ @Override public void splitRegion(byte[] bytes, byte[] bytes2) throws IOException { LOG.info("split is a no-op"); } /** {@inheritDoc} */ @Override public void shutdown() throws IOException { throw new UnsupportedOperationException("shutdown"); // TODO } /** {@inheritDoc} */ @Override public void stopMaster() throws IOException { throw new UnsupportedOperationException("stopMaster"); // TODO } /** {@inheritDoc} */ @Override public void stopRegionServer(String hostnamePort) throws IOException { throw new UnsupportedOperationException("stopRegionServer"); // TODO } /** {@inheritDoc} */ @Override public void createNamespace(NamespaceDescriptor descriptor) throws IOException { if (provideWarningsForNamespaces()) { LOG.warn("createNamespace is a no-op"); } else { throw new UnsupportedOperationException("createNamespace"); // TODO } } /** {@inheritDoc} */ @Override public void modifyNamespace(NamespaceDescriptor descriptor) throws IOException { if (provideWarningsForNamespaces()) { LOG.warn("modifyNamespace is a no-op"); } else { throw new UnsupportedOperationException("modifyNamespace"); // TODO } } /** {@inheritDoc} */ @Override public void deleteNamespace(String name) throws IOException { if (provideWarningsForNamespaces()) { LOG.warn("deleteNamespace is a no-op"); } else { throw new UnsupportedOperationException("deleteNamespace"); // TODO } } /** {@inheritDoc} */ @Override public NamespaceDescriptor getNamespaceDescriptor(String name) throws IOException { if (provideWarningsForNamespaces()) { LOG.warn("getNamespaceDescriptor is a no-op"); return null; } else { throw new UnsupportedOperationException("getNamespaceDescriptor"); // TODO } } /** {@inheritDoc} */ @Override public NamespaceDescriptor[] listNamespaceDescriptors() throws IOException { if (provideWarningsForNamespaces()) { LOG.warn("listNamespaceDescriptors is a no-op"); return new NamespaceDescriptor[0]; } else { throw new UnsupportedOperationException("listNamespaceDescriptors"); // TODO } } /** {@inheritDoc} */ @Override public HTableDescriptor[] listTableDescriptorsByNamespace(String name) throws IOException { if (provideWarningsForNamespaces()) { LOG.warn("listTableDescriptorsByNamespace is a no-op"); return new HTableDescriptor[0]; } else { throw new UnsupportedOperationException("listTableDescriptorsByNamespace"); // TODO } } /** {@inheritDoc} */ @Override public TableName[] listTableNamesByNamespace(String name) throws IOException { if (provideWarningsForNamespaces()) { LOG.warn("listTableNamesByNamespace is a no-op"); return new TableName[0]; } else { throw new UnsupportedOperationException("listTableNamesByNamespace"); // TODO } } private boolean provideWarningsForNamespaces() { return configuration.getBoolean(BigtableOptionsFactory.BIGTABLE_NAMESPACE_WARNING_KEY, false); } /** {@inheritDoc} */ @Override public String[] getMasterCoprocessors() { throw new UnsupportedOperationException("getMasterCoprocessors"); // TODO } /** {@inheritDoc} */ @Override public void execProcedure(String signature, String instance, Map props) throws IOException { throw new UnsupportedOperationException("execProcedure"); // TODO } /** {@inheritDoc} */ @Override public byte[] execProcedureWithRet(String signature, String instance, Map props) throws IOException { throw new UnsupportedOperationException("execProcedureWithRet"); // TODO } /** {@inheritDoc} */ @Override public boolean isProcedureFinished(String signature, String instance, Map props) throws IOException { throw new UnsupportedOperationException("isProcedureFinished"); // TODO } /** {@inheritDoc} */ @Override public CoprocessorRpcChannel coprocessorService() { throw new UnsupportedOperationException("coprocessorService"); // TODO } /** {@inheritDoc} */ @Override public CoprocessorRpcChannel coprocessorService(ServerName serverName) { throw new UnsupportedOperationException("coprocessorService"); // TODO } /** {@inheritDoc} */ @Override public void updateConfiguration(ServerName serverName) throws IOException { throw new UnsupportedOperationException("updateConfiguration"); // TODO } /** {@inheritDoc} */ @Override public void updateConfiguration() throws IOException { throw new UnsupportedOperationException("updateConfiguration"); // TODO } /** {@inheritDoc} */ @Override public int getMasterInfoPort() throws IOException { throw new UnsupportedOperationException("getMasterInfoPort"); // TODO } /** {@inheritDoc} */ @Override public void rollWALWriter(ServerName serverName) throws IOException, FailedLogCloseException { throw new UnsupportedOperationException("rollWALWriter"); // TODO } /** Handler for unsupported operations for generating Admin class at runtime. */ public static class UnsupportedOperationsHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { throw new UnsupportedOperationException(method.getName()); } } }