/** * Copyright 2010 The Apache Software Foundation * * 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 java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.NavigableMap; 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.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.HServerAddress; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MasterNotRunningException; import org.apache.hadoop.hbase.RegionException; import org.apache.hadoop.hbase.RemoteExceptionHandler; import org.apache.hadoop.hbase.TableExistsException; import org.apache.hadoop.hbase.UnknownRegionException; import org.apache.hadoop.hbase.ZooKeeperConnectionException; import org.apache.hadoop.hbase.catalog.CatalogTracker; import org.apache.hadoop.hbase.catalog.MetaReader; import org.apache.hadoop.hbase.ipc.HMasterInterface; import org.apache.hadoop.hbase.ipc.HRegionInterface; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Pair; import org.apache.hadoop.hbase.util.Writables; import org.apache.hadoop.ipc.RemoteException; /** * Provides an interface to manage HBase database table metadata + general * administrative functions. Use HBaseAdmin to create, drop, list, enable and * disable tables. Use it also to add and drop table column families. * * <p>See {@link HTable} to add, update, and delete data from an individual table. * <p>Currently HBaseAdmin instances are not expected to be long-lived. For * example, an HBaseAdmin instance will not ride over a Master restart. */ public class HBaseAdmin implements Abortable { private final Log LOG = LogFactory.getLog(this.getClass().getName()); // private final HConnection connection; final HConnection 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... Mutliply by this factor when // want to wait a long time. private final int retryLongerMultiplier; /** * Constructor * * @param conf Configuration object * @throws MasterNotRunningException if the master is not running * @throws ZooKeeperConnectionException if unable to connect to zookeeper */ public HBaseAdmin(Configuration conf) throws MasterNotRunningException, ZooKeeperConnectionException { this.connection = HConnectionManager.getConnection(conf); this.conf = conf; this.pause = conf.getLong("hbase.client.pause", 1000); this.numRetries = conf.getInt("hbase.client.retries.number", 10); this.retryLongerMultiplier = conf.getInt("hbase.client.retries.longer.multiplier", 10); this.connection.getMaster(); } /** * @return A new CatalogTracker instance; call {@link #cleanupCatalogTracker(CatalogTracker)} * to cleanup the returned catalog tracker. * @throws ZooKeeperConnectionException * @throws IOException * @see #cleanupCatalogTracker(CatalogTracker); */ private synchronized CatalogTracker getCatalogTracker() throws ZooKeeperConnectionException, IOException { CatalogTracker ct = null; try { HConnection connection = HConnectionManager.getConnection(new Configuration(this.conf)); ct = new CatalogTracker(connection); ct.start(); } catch (InterruptedException e) { // Let it out as an IOE for now until we redo all so tolerate IEs Thread.currentThread().interrupt(); throw new IOException("Interrupted", e); } return ct; } private void cleanupCatalogTracker(final CatalogTracker ct) { ct.stop(); HConnectionManager.deleteConnection(ct.getConnection().getConfiguration(), true); } @Override public void abort(String why, Throwable e) { // Currently does nothing but throw the passed message and exception throw new RuntimeException(why, e); } /** @return HConnection used by this object. */ public HConnection getConnection() { return connection; } /** * Get a connection to the currently set master. * @return proxy connection to master server for this instance * @throws MasterNotRunningException if the master is not running * @throws ZooKeeperConnectionException if unable to connect to zookeeper */ public HMasterInterface getMaster() throws MasterNotRunningException, ZooKeeperConnectionException { return this.connection.getMaster(); } /** @return - true if the master server is running * @throws ZooKeeperConnectionException * @throws MasterNotRunningException */ public boolean isMasterRunning() throws MasterNotRunningException, ZooKeeperConnectionException { return this.connection.isMasterRunning(); } /** * @param tableName Table to check. * @return True if table exists already. * @throws IOException */ public boolean tableExists(final String tableName) throws IOException { boolean b = false; CatalogTracker ct = getCatalogTracker(); try { b = MetaReader.tableExists(ct, tableName); } finally { cleanupCatalogTracker(ct); } return b; } /** * @param tableName Table to check. * @return True if table exists already. * @throws IOException */ public boolean tableExists(final byte [] tableName) throws IOException { return tableExists(Bytes.toString(tableName)); } /** * List all the userspace tables. In other words, scan the META table. * * If we wanted this to be really fast, we could implement a special * catalog table that just contains table names and their descriptors. * Right now, it only exists as part of the META table's region info. * * @return - returns an array of HTableDescriptors * @throws IOException if a remote or network exception occurs */ public HTableDescriptor[] listTables() throws IOException { return this.connection.listTables(); } /** * Method for getting the tableDescriptor * @param tableName as a byte [] * @return the tableDescriptor * @throws IOException if a remote or network exception occurs */ public HTableDescriptor getTableDescriptor(final byte [] tableName) throws IOException { return this.connection.getHTableDescriptor(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 */ 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 TableExistsException if table already exists (If concurrent * threads, the table may have been created between test-for-existence * and attempt-at-creation). * @throws IOException */ public void createTable(HTableDescriptor desc, byte [] startKey, byte [] endKey, int numRegions) throws IOException { HTableDescriptor.isLegalTableName(desc.getName()); 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 = 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 (the first region has a null start key and * the last region has a null end key). * Synchronous operation. * * @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 * @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 */ public void createTable(HTableDescriptor desc, byte [][] splitKeys) throws IOException { HTableDescriptor.isLegalTableName(desc.getName()); if(splitKeys != null && splitKeys.length > 1) { Arrays.sort(splitKeys, Bytes.BYTES_COMPARATOR); // Verify there are no duplicate split keys byte [] lastKey = null; for(byte [] splitKey : splitKeys) { 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; } } createTableAsync(desc, splitKeys); for (int tries = 0; tries < numRetries; tries++) { try { // Wait for new table to come on-line connection.locateRegion(desc.getName(), HConstants.EMPTY_START_ROW); break; } catch (RegionException e) { if (tries == numRetries - 1) { // Ran out of tries throw e; } } try { Thread.sleep(getPauseTime(tries)); } catch (InterruptedException e) { // Just continue; ignore the interruption. } } } /** * Creates a new table but does not block and wait for it to come online. * Asynchronous operation. * * @param desc table descriptor for table * * @throws IllegalArgumentException Bad table name. * @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 */ public void createTableAsync(HTableDescriptor desc, byte [][] splitKeys) throws IOException { HTableDescriptor.isLegalTableName(desc.getName()); try { getMaster().createTable(desc, splitKeys); } catch (RemoteException e) { throw e.unwrapRemoteException(); } } /** * Deletes a table. * Synchronous operation. * * @param tableName name of table to delete * @throws IOException if a remote or network exception occurs */ public void deleteTable(final String tableName) throws IOException { deleteTable(Bytes.toBytes(tableName)); } /** * Deletes a table. * Synchronous operation. * * @param tableName name of table to delete * @throws IOException if a remote or network exception occurs */ public void deleteTable(final byte [] tableName) throws IOException { isMasterRunning(); HTableDescriptor.isLegalTableName(tableName); HRegionLocation firstMetaServer = getFirstMetaServerForTable(tableName); try { getMaster().deleteTable(tableName); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } final int batchCount = this.conf.getInt("hbase.admin.scanner.caching", 10); // Wait until all regions deleted HRegionInterface server = connection.getHRegionConnection(firstMetaServer.getServerAddress()); HRegionInfo info = new HRegionInfo(); for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) { long scannerId = -1L; try { Scan scan = new Scan().addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER); scannerId = server.openScanner( firstMetaServer.getRegionInfo().getRegionName(), scan); // Get a batch at a time. Result [] values = server.next(scannerId, batchCount); if (values == null || values.length == 0) { break; } boolean found = false; for (Result r : values) { NavigableMap<byte[], byte[]> infoValues = r.getFamilyMap(HConstants.CATALOG_FAMILY); for (Map.Entry<byte[], byte[]> e : infoValues.entrySet()) { if (Bytes.equals(e.getKey(), HConstants.REGIONINFO_QUALIFIER)) { info = (HRegionInfo) Writables.getWritable(e.getValue(), info); if (Bytes.equals(info.getTableDesc().getName(), tableName)) { found = true; } else { found = false; break; } } } } if (!found) { break; } } catch (IOException ex) { if(tries == numRetries - 1) { // no more tries left if (ex instanceof RemoteException) { ex = RemoteExceptionHandler.decodeRemoteException((RemoteException) ex); } throw ex; } } finally { if (scannerId != -1L) { try { server.close(scannerId); } catch (Exception ex) { LOG.warn(ex); } } } try { Thread.sleep(getPauseTime(tries)); } catch (InterruptedException e) { // continue } } // Delete cached information to prevent clients from using old locations this.connection.clearRegionCache(tableName); LOG.info("Deleted " + Bytes.toString(tableName)); } public void enableTable(final String tableName) throws IOException { enableTable(Bytes.toBytes(tableName)); } /** * Enable a table. May timeout. Use {@link #enableTableAsync(byte[])} * and {@link #isTableEnabled(byte[])} instead. * @param tableName name of the table * @throws IOException if a remote or network exception occurs * @see #isTableEnabled(byte[]) * @see #disableTable(byte[]) * @see #enableTableAsync(byte[]) */ public void enableTable(final byte [] tableName) throws IOException { enableTableAsync(tableName); // Wait until all regions are enabled boolean enabled = false; for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) { enabled = isTableEnabled(tableName); if (enabled) { break; } long sleep = getPauseTime(tries); if (LOG.isDebugEnabled()) { LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " + "enabled in " + Bytes.toString(tableName)); } try { Thread.sleep(sleep); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // Do this conversion rather than let it out because do not want to // change the method signature. throw new IOException("Interrupted", e); } } if (!enabled) { throw new IOException("Unable to enable table " + Bytes.toString(tableName)); } LOG.info("Enabled table " + Bytes.toString(tableName)); } public void enableTableAsync(final String tableName) throws IOException { enableTableAsync(Bytes.toBytes(tableName)); } /** * 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 * @since 0.90.0 */ public void enableTableAsync(final byte [] tableName) throws IOException { isMasterRunning(); try { getMaster().enableTable(tableName); } catch (RemoteException e) { throw e.unwrapRemoteException(); } LOG.info("Started enable of " + Bytes.toString(tableName)); } public void disableTableAsync(final String tableName) throws IOException { disableTableAsync(Bytes.toBytes(tableName)); } /** * 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 */ public void disableTableAsync(final byte [] tableName) throws IOException { isMasterRunning(); try { getMaster().disableTable(tableName); } catch (RemoteException e) { throw e.unwrapRemoteException(); } LOG.info("Started disable of " + Bytes.toString(tableName)); } public void disableTable(final String tableName) throws IOException { disableTable(Bytes.toBytes(tableName)); } /** * Disable table and wait on completion. May timeout eventually. Use * {@link #disableTableAsync(byte[])} and {@link #isTableDisabled(String)} * instead. * @param tableName * @throws IOException */ public void disableTable(final byte [] tableName) throws IOException { disableTableAsync(tableName); // Wait until table is disabled boolean disabled = false; for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) { disabled = isTableDisabled(tableName); if (disabled) { break; } long sleep = getPauseTime(tries); if (LOG.isDebugEnabled()) { LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " + "disabled in " + Bytes.toString(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. Thread.currentThread().interrupt(); throw new IOException("Interrupted", e); } } if (!disabled) { throw new RegionException("Retries exhausted, it took too long to wait"+ " for the table " + Bytes.toString(tableName) + " to be disabled."); } LOG.info("Disabled " + Bytes.toString(tableName)); } /** * @param tableName name of table to check * @return true if table is on-line * @throws IOException if a remote or network exception occurs */ public boolean isTableEnabled(String tableName) throws IOException { return isTableEnabled(Bytes.toBytes(tableName)); } /** * @param tableName name of table to check * @return true if table is on-line * @throws IOException if a remote or network exception occurs */ public boolean isTableEnabled(byte[] tableName) throws IOException { return connection.isTableEnabled(tableName); } /** * @param tableName name of table to check * @return true if table is off-line * @throws IOException if a remote or network exception occurs */ public boolean isTableDisabled(final String tableName) throws IOException { return isTableDisabled(Bytes.toBytes(tableName)); } /** * @param tableName name of table to check * @return true if table is off-line * @throws IOException if a remote or network exception occurs */ public boolean isTableDisabled(byte[] tableName) throws IOException { return connection.isTableDisabled(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 */ public boolean isTableAvailable(byte[] tableName) throws IOException { return connection.isTableAvailable(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 */ public boolean isTableAvailable(String tableName) throws IOException { return connection.isTableAvailable(Bytes.toBytes(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 String tableName, HColumnDescriptor column) throws IOException { addColumn(Bytes.toBytes(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 byte [] tableName, HColumnDescriptor column) throws IOException { HTableDescriptor.isLegalTableName(tableName); try { getMaster().addColumn(tableName, column); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } } /** * 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(Bytes.toBytes(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 byte [] tableName, final byte [] columnName) throws IOException { try { getMaster().deleteColumn(tableName, columnName); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } } /** * Modify an existing column family on a table. * Asynchronous operation. * * @param tableName name of table * @param columnName name of column to be modified * @param descriptor new column descriptor to use * @throws IOException if a remote or network exception occurs * @deprecated The <code>columnName</code> is redundant. Use {@link #addColumn(String, HColumnDescriptor)} */ public void modifyColumn(final String tableName, final String columnName, HColumnDescriptor descriptor) throws IOException { modifyColumn(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 String tableName, HColumnDescriptor descriptor) throws IOException { modifyColumn(Bytes.toBytes(tableName), descriptor); } /** * Modify an existing column family on a table. * Asynchronous operation. * * @param tableName name of table * @param columnName name of column to be modified * @param descriptor new column descriptor to use * @throws IOException if a remote or network exception occurs * @deprecated The <code>columnName</code> is redundant. Use {@link #modifyColumn(byte[], HColumnDescriptor)} */ public void modifyColumn(final byte [] tableName, final byte [] columnName, HColumnDescriptor descriptor) throws IOException { modifyColumn(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 { try { getMaster().modifyColumn(tableName, descriptor); } catch (RemoteException re) { // Convert RE exceptions in here; client shouldn't have to deal with them, // at least w/ the type of exceptions that come out of this method: // TableNotFoundException, etc. throw RemoteExceptionHandler.decodeRemoteException(re); } } /** * 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 hostAndPort If supplied, we'll use this location rather than * the one currently in <code>.META.</code> * @throws IOException if a remote or network exception occurs */ public void closeRegion(final String regionname, final String hostAndPort) throws IOException { closeRegion(Bytes.toBytes(regionname), hostAndPort); } /** * 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 hostAndPort If supplied, we'll use this location rather than * the one currently in <code>.META.</code> * @throws IOException if a remote or network exception occurs */ public void closeRegion(final byte [] regionname, final String hostAndPort) throws IOException { CatalogTracker ct = getCatalogTracker(); try { if (hostAndPort != null) { HServerAddress hsa = new HServerAddress(hostAndPort); Pair<HRegionInfo, HServerAddress> pair = MetaReader.getRegion(ct, regionname); if (pair == null || pair.getSecond() == null) { LOG.info("No server in .META. for " + Bytes.toString(regionname) + "; pair=" + pair); } else { closeRegion(hsa, pair.getFirst()); } } else { Pair<HRegionInfo, HServerAddress> pair = MetaReader.getRegion(ct, regionname); if (pair == null || pair.getSecond() == null) { LOG.info("No server in .META. for " + Bytes.toString(regionname) + "; pair=" + pair); } else { closeRegion(pair.getSecond(), pair.getFirst()); } } } finally { cleanupCatalogTracker(ct); } } private void closeRegion(final HServerAddress hsa, final HRegionInfo hri) throws IOException { HRegionInterface rs = this.connection.getHRegionConnection(hsa); // Close the region without updating zk state. rs.closeRegion(hri, false); } /** * Flush a table or an individual region. * Asynchronous operation. * * @param tableNameOrRegionName table or region to flush * @throws IOException if a remote or network exception occurs * @throws InterruptedException */ public void flush(final String tableNameOrRegionName) throws IOException, InterruptedException { flush(Bytes.toBytes(tableNameOrRegionName)); } /** * Flush a table or an individual region. * Asynchronous operation. * * @param tableNameOrRegionName table or region to flush * @throws IOException if a remote or network exception occurs * @throws InterruptedException */ public void flush(final byte [] tableNameOrRegionName) throws IOException, InterruptedException { boolean isRegionName = isRegionName(tableNameOrRegionName); CatalogTracker ct = getCatalogTracker(); try { if (isRegionName) { Pair<HRegionInfo, HServerAddress> pair = MetaReader.getRegion(getCatalogTracker(), tableNameOrRegionName); if (pair == null || pair.getSecond() == null) { LOG.info("No server in .META. for " + Bytes.toString(tableNameOrRegionName) + "; pair=" + pair); } else { flush(pair.getSecond(), pair.getFirst()); } } else { List<Pair<HRegionInfo, HServerAddress>> pairs = MetaReader.getTableRegionsAndLocations(getCatalogTracker(), Bytes.toString(tableNameOrRegionName)); for (Pair<HRegionInfo, HServerAddress> pair: pairs) { if (pair.getSecond() == null) continue; flush(pair.getSecond(), pair.getFirst()); } } } finally { cleanupCatalogTracker(ct); } } private void flush(final HServerAddress hsa, final HRegionInfo hri) throws IOException { HRegionInterface rs = this.connection.getHRegionConnection(hsa); rs.flushRegion(hri); } /** * Compact a table or an individual region. * Asynchronous operation. * * @param tableNameOrRegionName table or region to compact * @throws IOException if a remote or network exception occurs * @throws InterruptedException */ public void compact(final String tableNameOrRegionName) throws IOException, InterruptedException { compact(Bytes.toBytes(tableNameOrRegionName)); } /** * Compact a table or an individual region. * Asynchronous operation. * * @param tableNameOrRegionName table or region to compact * @throws IOException if a remote or network exception occurs * @throws InterruptedException */ public void compact(final byte [] tableNameOrRegionName) throws IOException, InterruptedException { compact(tableNameOrRegionName, false); } /** * Major compact a table or an individual region. * Asynchronous operation. * * @param tableNameOrRegionName table or region to major compact * @throws IOException if a remote or network exception occurs * @throws InterruptedException */ public void majorCompact(final String tableNameOrRegionName) throws IOException, InterruptedException { majorCompact(Bytes.toBytes(tableNameOrRegionName)); } /** * Major compact a table or an individual region. * Asynchronous operation. * * @param tableNameOrRegionName table or region to major compact * @throws IOException if a remote or network exception occurs * @throws InterruptedException */ public void majorCompact(final byte [] tableNameOrRegionName) throws IOException, InterruptedException { compact(tableNameOrRegionName, true); } /** * Compact a table or an individual region. * Asynchronous operation. * * @param tableNameOrRegionName table or region to compact * @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 byte [] tableNameOrRegionName, final boolean major) throws IOException, InterruptedException { CatalogTracker ct = getCatalogTracker(); try { if (isRegionName(tableNameOrRegionName)) { Pair<HRegionInfo, HServerAddress> pair = MetaReader.getRegion(ct, tableNameOrRegionName); if (pair == null || pair.getSecond() == null) { LOG.info("No server in .META. for " + Bytes.toString(tableNameOrRegionName) + "; pair=" + pair); } else { compact(pair.getSecond(), pair.getFirst(), major); } } else { List<Pair<HRegionInfo, HServerAddress>> pairs = MetaReader.getTableRegionsAndLocations(ct, Bytes.toString(tableNameOrRegionName)); for (Pair<HRegionInfo, HServerAddress> pair: pairs) { if (pair.getSecond() == null) continue; compact(pair.getSecond(), pair.getFirst(), major); } } } finally { cleanupCatalogTracker(ct); } } private void compact(final HServerAddress hsa, final HRegionInfo hri, final boolean major) throws IOException { HRegionInterface rs = this.connection.getHRegionConnection(hsa); rs.compactRegion(hri, major); } /** * Move the region <code>r</code> to <code>dest</code>. * @param encodedRegionName The encoded region name; i.e. the hash that makes * up the region name suffix: e.g. if regionname is * <code>TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396.</code>, * then the encoded region name is: <code>527db22f95c8a9e0116f0cc13c680396</code>. * @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: * <code> host187.example.com,60020,1289493121758</code>. * @throws UnknownRegionException Thrown if we can't find a region named * <code>encodedRegionName</code> * @throws ZooKeeperConnectionException * @throws MasterNotRunningException */ public void move(final byte [] encodedRegionName, final byte [] destServerName) throws UnknownRegionException, MasterNotRunningException, ZooKeeperConnectionException { getMaster().move(encodedRegionName, destServerName); } /** * @param regionName Region name to assign. * @param force True to force assign. * @throws MasterNotRunningException * @throws ZooKeeperConnectionException * @throws IOException */ public void assign(final byte [] regionName, final boolean force) throws MasterNotRunningException, ZooKeeperConnectionException, IOException { getMaster().assign(regionName, force); } /** * 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). * @throws MasterNotRunningException * @throws ZooKeeperConnectionException * @throws IOException */ public void unassign(final byte [] regionName, final boolean force) throws MasterNotRunningException, ZooKeeperConnectionException, IOException { getMaster().unassign(regionName, force); } /** * Turn the load balancer on or off. * @param b If true, enable balancer. If false, disable balancer. * @return Previous balancer value */ public boolean balanceSwitch(final boolean b) throws MasterNotRunningException, ZooKeeperConnectionException { return getMaster().balanceSwitch(b); } /** * 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. */ public boolean balancer() throws MasterNotRunningException, ZooKeeperConnectionException { return getMaster().balance(); } /** * Split a table or an individual region. * Asynchronous operation. * * @param tableNameOrRegionName table or region to split * @throws IOException if a remote or network exception occurs * @throws InterruptedException */ public void split(final String tableNameOrRegionName) throws IOException, InterruptedException { split(Bytes.toBytes(tableNameOrRegionName)); } /** * Split a table or an individual region. * Asynchronous operation. * * @param tableNameOrRegionName table to region to split * @throws IOException if a remote or network exception occurs * @throws InterruptedException */ public void split(final byte [] tableNameOrRegionName) throws IOException, InterruptedException { CatalogTracker ct = getCatalogTracker(); try { if (isRegionName(tableNameOrRegionName)) { // Its a possible region name. Pair<HRegionInfo, HServerAddress> pair = MetaReader.getRegion(getCatalogTracker(), tableNameOrRegionName); if (pair == null || pair.getSecond() == null) { LOG.info("No server in .META. for " + Bytes.toString(tableNameOrRegionName) + "; pair=" + pair); } else { split(pair.getSecond(), pair.getFirst()); } } else { List<Pair<HRegionInfo, HServerAddress>> pairs = MetaReader.getTableRegionsAndLocations(getCatalogTracker(), Bytes.toString(tableNameOrRegionName)); for (Pair<HRegionInfo, HServerAddress> pair: pairs) { // May not be a server for a particular row if (pair.getSecond() == null) continue; split(pair.getSecond(), pair.getFirst()); } } } finally { cleanupCatalogTracker(ct); } } private void split(final HServerAddress hsa, final HRegionInfo hri) throws IOException { HRegionInterface rs = this.connection.getHRegionConnection(hsa); rs.splitRegion(hri); } /** * 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 */ public void modifyTable(final byte [] tableName, HTableDescriptor htd) throws IOException { try { getMaster().modifyTable(tableName, htd); } catch (RemoteException re) { // Convert RE exceptions in here; client shouldn't have to deal with them, // at least w/ the type of exceptions that come out of this method: // TableNotFoundException, etc. throw RemoteExceptionHandler.decodeRemoteException(re); } } /** * @param tableNameOrRegionName Name of a table or name of a region. * @return True if <code>tableNameOrRegionName</code> is *possibly* a region * name else false if a verified tablename (we call {@link #tableExists(byte[])}; * else we throw an exception. * @throws IOException */ private boolean isRegionName(final byte [] tableNameOrRegionName) throws IOException { if (tableNameOrRegionName == null) { throw new IllegalArgumentException("Pass a table name or region name"); } return !tableExists(tableNameOrRegionName); } /** * Shuts down the HBase cluster * @throws IOException if a remote or network exception occurs */ public synchronized void shutdown() throws IOException { isMasterRunning(); try { getMaster().shutdown(); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } } /** * Shuts down the current HBase master only. * Does not shutdown the cluster. * @see #shutdown() * @throws IOException if a remote or network exception occurs */ public synchronized void stopMaster() throws IOException { isMasterRunning(); try { getMaster().stopMaster(); } catch (RemoteException e) { throw RemoteExceptionHandler.decodeRemoteException(e); } } /** * Stop the designated regionserver. * @throws IOException if a remote or network exception occurs */ public synchronized void stopRegionServer(final HServerAddress hsa) throws IOException { HRegionInterface rs = this.connection.getHRegionConnection(hsa); rs.stop("Called by admin client " + this.connection.toString()); } /** * @return cluster status * @throws IOException if a remote or network exception occurs */ public ClusterStatus getClusterStatus() throws IOException { return getMaster().getClusterStatus(); } private HRegionLocation getFirstMetaServerForTable(final byte [] tableName) throws IOException { return connection.locateRegion(HConstants.META_TABLE_NAME, HRegionInfo.createRegionName(tableName, null, HConstants.NINES, false)); } /** * @return Configuration used by the instance. */ public Configuration getConfiguration() { return this.conf; } /** * 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 */ public static void checkHBaseAvailable(Configuration conf) throws MasterNotRunningException, ZooKeeperConnectionException { Configuration copyOfConf = HBaseConfiguration.create(conf); copyOfConf.setInt("hbase.client.retries.number", 1); new HBaseAdmin(copyOfConf); } }