blob: 9ac571b51ac63fe2bca99f591b3f7a2e23b5ad88 [file] [log] [blame]
/*
* 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.hbase;
import static org.apache.hadoop.hbase.HConstants.CATALOG_FAMILY;
import static org.apache.hadoop.hbase.HConstants.REGIONINFO_QUALIFIER;
import static org.apache.hadoop.hbase.HConstants.TABLE_FAMILY;
import static org.apache.hadoop.hbase.HConstants.TABLE_STATE_QUALIFIER;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.SortedMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellBuilder;
import org.apache.hadoop.hbase.CellBuilderFactory;
import org.apache.hadoop.hbase.CellBuilderType;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HRegionLocation;
import org.apache.hadoop.hbase.RegionLocations;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.client.RegionInfoBuilder;
import org.apache.hadoop.hbase.client.RegionLocator;
import org.apache.hadoop.hbase.client.RegionReplicaUtil;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.client.TableState;
import org.apache.hadoop.hbase.exceptions.DeserializationException;
import org.apache.hadoop.hbase.master.RegionState;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.hadoop.hbase.util.PairOfSameType;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
/**
* hbck's local version of the MetaTableAccessor from the hbase repo
* A Utility class to facilitate hbck2's access to Meta table.
*/
@InterfaceAudience.Private
public final class HBCKMetaTableAccessor {
/** The delimiter for meta columns for replicaIds > 0 */
static final char META_REPLICA_ID_DELIMITER = '_';
/** A regex for parsing server columns from meta. See above javadoc for meta layout */
private static final Pattern SERVER_COLUMN_PATTERN
= Pattern.compile("^server(_[0-9a-fA-F]{4})?$");
/**
* Private constructor to keep this class from being instantiated.
*/
private HBCKMetaTableAccessor() {
}
private static final Logger LOG = LoggerFactory.getLogger(
HBCKMetaTableAccessor.class);
// Constants
private static final byte[] MERGE_QUALIFIER_PREFIX = Bytes.toBytes("merge");
/** COPIED MetaTableAccessor.getMergeRegions */
public static List<RegionInfo> getMergeRegions(Cell[] cells) {
if (cells == null) {
return null;
}
List<RegionInfo> regionsToMerge = null;
for (Cell cell : cells) {
if (!isMergeQualifierPrefix(cell)) {
continue;
}
// Ok. This cell is that of a info:merge* column.
RegionInfo ri = RegionInfo
.parseFromOrNull(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
if (ri != null) {
if (regionsToMerge == null) {
regionsToMerge = new ArrayList<>();
}
regionsToMerge.add(ri);
}
}
return regionsToMerge;
}
/**
* Delete the passed <code>RegionInfo</code> from the <code>hbase:meta</code> table.
*
* @param connection connection we're using
* @param regionInfo the regionInfo to delete from the meta table
* @throws IOException if it's not able to delete the regionInfo
*/
public static void deleteRegionInfo(Connection connection, RegionInfo regionInfo)
throws IOException {
Delete delete = new Delete(regionInfo.getRegionName());
delete.addFamily(HConstants.CATALOG_FAMILY, HConstants.LATEST_TIMESTAMP);
deleteFromMetaTable(connection, delete);
LOG.info("Deleted {}", regionInfo.getRegionNameAsString());
}
/**
* Delete the passed <code>RegionInfo</code> from the <code>hbase:meta</code> table.
*
* @param connection connection we're using
* @param regionInfo the regionInfo to delete from the meta table
* @throws IOException if it's not able to delete the regionInfo
*/
public static void deleteRegionInfoColumn(Connection connection, RegionInfo regionInfo)
throws IOException {
Delete delete = new Delete(regionInfo.getRegionName());
delete.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
HConstants.LATEST_TIMESTAMP);
deleteFromMetaTable(connection, delete);
LOG.info("Deleted regioninfo for {}", regionInfo.getRegionNameAsString());
}
/**
* Delete the passed <code>HBCKMetaEntry</code> from the <code>hbase:meta</code> table.
*
* @param connection connection we're using
* @param region the region to be deleted from the meta table
* @throws IOException if it's not able to delete the regionInfo
*/
public static void deleteRegion(Connection connection, HBCKMetaEntry region)
throws IOException {
Delete delete = new Delete(region.getRegionName());
delete.addFamily(HConstants.CATALOG_FAMILY, HConstants.LATEST_TIMESTAMP);
deleteFromMetaTable(connection, delete);
LOG.info("Deleted {}", region.getEncodedRegionName());
}
// Private helper methods
// COPIED from MetaTableAccessor.isMergeQualifierPrefix()
private static boolean isMergeQualifierPrefix(Cell cell) {
// Check to see if has family and that qualifier starts with the MERGE_QUALIFIER_PREFIX
return CellUtil.matchingFamily(cell, HConstants.CATALOG_FAMILY) && qualifierStartsWith(cell,
MERGE_QUALIFIER_PREFIX);
}
/**
* Finds if the start of the qualifier part of the Cell matches 'startsWith'
*
* COPIED from PrivateCellUtil.qualifierStartsWith()
* @param left the cell with which we need to match the qualifier
* @param startsWith the serialized keyvalue format byte[]
* @return true if the qualifier have same staring characters, false otherwise
*/
private static boolean qualifierStartsWith(final Cell left, final byte[] startsWith) {
if (startsWith == null || startsWith.length == 0) {
throw new IllegalArgumentException("Cannot pass an empty startsWith");
}
return Bytes
.equals(left.getQualifierArray(), left.getQualifierOffset(), startsWith.length, startsWith,
0, startsWith.length);
}
/**
* Delete the passed <code>d</code> from the <code>hbase:meta</code> table.
*
* COPIED MetaTableAccessor.deleteFromMetaTable()
* @param connection connection we're using
* @param d Delete to add to hbase:meta
*/
private static void deleteFromMetaTable(final Connection connection, final Delete d)
throws IOException {
if (connection == null) {
throw new NullPointerException("No connection");
} else if (connection.isClosed()) {
throw new IOException("connection is closed");
}
try (Table t = connection.getTable(TableName.META_TABLE_NAME)) {
List<Delete> deletes = new ArrayList<>();
deletes.add(d);
LOG.debug("Add {} delete to meta table", deletes);
t.delete(deletes);
}
}
/**
* Returns all regions in meta for the given table.
* @param conn a valid, open connection.
* @param table the table to list regions in meta.
* @return a list of <code>HBCKMetaEntry</code> with encoded region names, and the meta row key
* for all table regions present in meta.
* @throws IOException on any issues related with scanning meta table
* */
public static List<HBCKMetaEntry> getTableRegionsAsMetaEntries(final Connection conn,
final TableName table) throws IOException {
final MetaScanner<HBCKMetaEntry> scanner = new MetaScanner<>();
final String startRow = Bytes.toString(table.getName()) + ",,";
final String stopRow = Bytes.toString(table.getName()) + " ,,";
return scanner.scanMeta(conn,
scan -> {
scan.withStartRow(Bytes.toBytes(startRow));
scan.withStopRow(Bytes.toBytes(stopRow));
},
r -> {
if (r.getRow() != null) {
boolean encodedNameOffset = false;
StringBuilder encodedNameBuilder = new StringBuilder();
for(int i=0; i<r.getRow().length; i++){
if (r.getRow()[i]=='.'){
encodedNameOffset = !encodedNameOffset;
continue;
}
if(encodedNameOffset){
encodedNameBuilder.append((char)r.getRow()[i]);
}
}
return new HBCKMetaEntry(r.getRow(), encodedNameBuilder.toString());
}
return null;
});
}
/**
* Returns all regions in meta for the given table.
* @param conn a valid, open connection.
* @param table the table to list regions in meta.
* @return a list of <code>String</code> of encoded region names,
* for all table regions present in meta.
* @throws IOException on any issues related with scanning meta table
*/
public static List<RegionInfo> getTableRegions(final Connection conn, final TableName table)
throws IOException {
final MetaScanner<RegionInfo> scanner = new MetaScanner<>();
final String startRow = Bytes.toString(table.getName()) + ",,";
final String stopRow = Bytes.toString(table.getName()) + " ,,";
return scanner.scanMeta(conn,
scan -> {
scan.withStartRow(Bytes.toBytes(startRow));
scan.withStopRow(Bytes.toBytes(stopRow));
},
r -> {
Cell cell = r.getColumnLatestCell(CATALOG_FAMILY, REGIONINFO_QUALIFIER);
if(cell != null) {
RegionInfo info = RegionInfo
.parseFromOrNull(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
return info;
}
return null;
});
}
/**
* Returns a list of all tables having related entries on meta.
* @param conn a valid, open connection.
* @return a list of <code>TableName</code> for each table having at least one entry in meta.
* @throws IOException on any issues related with scanning meta table
*/
public static List<TableName> getTables(final Connection conn) throws IOException {
MetaScanner<TableName> scanner = new MetaScanner<>();
return scanner.scanMeta(conn,
scan -> scan.addColumn(TABLE_FAMILY, TABLE_STATE_QUALIFIER),
r -> {
final byte[] rowBytes = r.getRow();
String table = Bytes.toString(rowBytes);
if(table.lastIndexOf(HConstants.DELIMITER)>0) {
table = table.substring(0, table.lastIndexOf(HConstants.DELIMITER));
}
return TableName.valueOf(table);
});
}
/**
* Converts and adds the passed <code>RegionInfo</code> parameter into a valid 'info:regioninfo'
* cell value in 'hbase:meta'.
* @param conn a valid, open connection.
* @param region the region to be inserted in meta.
* @throws IOException on any issues related with scanning meta table
*/
public static void addRegionToMeta(Connection conn, RegionInfo region) throws IOException {
Put put = makePutFromRegionInfo(region,
System.currentTimeMillis());
addRegionStateToPut(put, RegionState.State.CLOSED);
conn.getTable(TableName.META_TABLE_NAME).put(put);
}
/**
* List all valid regions currently in META, excluding parent whoese been completely split.
* @param conn a valid, open connection.
* @return a list of all regions in META, excluding split parents.
* @throws IOException on any issues related with scanning meta table
*/
public static List<RegionInfo> getAllRegions(Connection conn) throws IOException {
MetaScanner<RegionInfo> scanner = new MetaScanner<>();
return scanner.scanMeta(conn,
scan -> scan.addColumn(HConstants.CATALOG_FAMILY, HConstants.STATE_QUALIFIER),
r -> {
Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY,
HConstants.REGIONINFO_QUALIFIER);
RegionInfo info = RegionInfo.parseFromOrNull(cell.getValueArray(),
cell.getValueOffset(), cell.getValueLength());
return info.isSplit() ? null : info;
});
}
/**
* Scans all "table:state" cell values existing in meta and returns as a map of
* <code>TableName</code> as key and <code>TableState</code> as the value.
* @param conn a valid, open connection.
* @return a Map of <code>TableName</code> as key and <code>TableState</code> as the value.
* @throws IOException on any issues related with scanning meta table
*/
public static Map<TableName, TableState> getAllTablesStates(Connection conn) throws IOException {
final MetaScanner<Pair<TableName, TableState>> scanner = new MetaScanner<>();
final Map<TableName, TableState> resultMap = new HashMap<>();
scanner.scanMeta(conn,
scan -> scan.addColumn(TABLE_FAMILY, TABLE_STATE_QUALIFIER),
r -> {
try {
TableState state = getTableState(r);
TableName name = TableName.valueOf(r.getRow());
resultMap.put(name, state);
} catch (IOException e) {
LOG.error(e.getMessage());
}
return null;
});
return resultMap;
}
/**
* (Copied partially from MetaTableAccessor)
* @param tableName table we're working with
* @return start row for scanning META according to query type
*/
public static byte[] getTableStartRowForMeta(TableName tableName) {
if (tableName == null) {
return null;
}
byte[] startRow = new byte[tableName.getName().length + 2];
System.arraycopy(tableName.getName(), 0, startRow, 0,
tableName.getName().length);
startRow[startRow.length - 2] = HConstants.DELIMITER;
startRow[startRow.length - 1] = HConstants.DELIMITER;
return startRow;
}
/**
* @param tableName table we're working with
* @return stop row for scanning META according to query type
*/
public static byte[] getTableStopRowForMeta(TableName tableName) {
if (tableName == null) {
return null;
}
final byte[] stopRow;
stopRow = new byte[tableName.getName().length + 3];
System.arraycopy(tableName.getName(), 0, stopRow, 0,
tableName.getName().length);
stopRow[stopRow.length - 3] = ' ';
stopRow[stopRow.length - 2] = HConstants.DELIMITER;
stopRow[stopRow.length - 1] = HConstants.DELIMITER;
return stopRow;
}
/** Returns the row key to use for this regionInfo */
public static byte[] getMetaKeyForRegion(RegionInfo regionInfo) {
if (regionInfo.isMetaRegion()) {
return RegionInfoBuilder.newBuilder(regionInfo.getTable())
.setRegionId(regionInfo.getRegionId())
.setReplicaId(0)
.setOffline(regionInfo.isOffline())
.build().getRegionName();
} else {
return RegionInfoBuilder.newBuilder(regionInfo.getTable())
.setStartKey(regionInfo.getStartKey())
.setEndKey(regionInfo.getEndKey())
.setSplit(regionInfo.isSplit())
.setRegionId(regionInfo.getRegionId())
.setReplicaId(0)
.setOffline(regionInfo.isOffline())
.build().getRegionName();
}
}
public static Put addLocation(Put p, ServerName sn, long openSeqNum, int replicaId)
throws IOException {
CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY);
return p.add(builder.clear()
.setRow(p.getRow())
.setFamily(CATALOG_FAMILY)
.setQualifier(getServerColumn(replicaId))
.setTimestamp(p.getTimestamp())
.setType(Cell.Type.Put)
.setValue(Bytes.toBytes(sn.getAddress().toString()))
.build())
.add(builder.clear()
.setRow(p.getRow())
.setFamily(CATALOG_FAMILY)
.setQualifier(getStartCodeColumn(replicaId))
.setTimestamp(p.getTimestamp())
.setType(Cell.Type.Put)
.setValue(Bytes.toBytes(sn.getStartcode()))
.build())
.add(builder.clear()
.setRow(p.getRow())
.setFamily(CATALOG_FAMILY)
.setQualifier(getSeqNumColumn(replicaId))
.setTimestamp(p.getTimestamp())
.setType(Cell.Type.Put)
.setValue(Bytes.toBytes(openSeqNum))
.build());
}
/**
* Count regions in <code>hbase:meta</code> for passed table.
* @param connection Connection object
* @param tableName table name to count regions for
* @return Count or regions in table <code>tableName</code>
*/
public static int getRegionCount(final Connection connection, final TableName tableName)
throws IOException {
try (RegionLocator locator = connection.getRegionLocator(tableName)) {
List<HRegionLocation> locations = locator.getAllRegionLocations();
return locations == null ? 0 : locations.size();
}
}
/**
* Decode table state from META Result.
* Should contain cell from HConstants.TABLE_FAMILY
* (Copied from MetaTableAccessor)
* @return null if not found
*/
public static TableState getTableState(Result r) throws IOException {
Cell cell = r.getColumnLatestCell(TABLE_FAMILY, TABLE_STATE_QUALIFIER);
if (cell == null) {
return null;
}
try {
return TableState.parseFrom(TableName.valueOf(r.getRow()),
Arrays.copyOfRange(cell.getValueArray(), cell.getValueOffset(),
cell.getValueOffset() + cell.getValueLength()));
} catch (DeserializationException e) {
throw new IOException(e);
}
}
/**
* Fetch table state for given table from META table
* @param conn connection to use
* @param tableName table to fetch state for
*/
public static TableState getTableState(Connection conn, TableName tableName)
throws IOException {
if (tableName.equals(TableName.META_TABLE_NAME)) {
return new TableState(tableName, TableState.State.ENABLED);
}
Table metaHTable = conn.getTable(TableName.META_TABLE_NAME);
Get get = new Get(tableName.getName()).addColumn(TABLE_FAMILY, TABLE_STATE_QUALIFIER);
Result result = metaHTable.get(get);
return getTableState(result);
}
/**
* Construct PUT for given state
* @param state new state
*/
public static Put makePutFromTableState(TableState state, long ts) {
Put put = new Put(state.getTableName().getName(), ts);
put.addColumn(TABLE_FAMILY, TABLE_STATE_QUALIFIER,
state.convert().toByteArray());
return put;
}
/**
* Generates and returns a Put containing the region into for the catalog table
*/
public static Put makePutFromRegionInfo(RegionInfo region, long ts) throws IOException {
Put put = new Put(region.getRegionName(), ts);
//copied from MetaTableAccessor
put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
.setRow(put.getRow())
.setFamily(HConstants.CATALOG_FAMILY)
.setQualifier(HConstants.REGIONINFO_QUALIFIER)
.setTimestamp(put.getTimestamp())
.setType(Cell.Type.Put)
// Serialize the Default Replica HRI otherwise scan of hbase:meta
// shows an info:regioninfo value with encoded name and region
// name that differs from that of the hbase;meta row.
.setValue(RegionInfo.toByteArray(RegionReplicaUtil.getRegionInfoForDefaultReplica(region)))
.build());
return put;
}
private static void addRegionStateToPut(Put put, RegionState.State state) throws IOException {
put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
.setRow(put.getRow())
.setFamily(HConstants.CATALOG_FAMILY)
.setQualifier(HConstants.STATE_QUALIFIER)
.setTimestamp(put.getTimestamp())
.setType(Cell.Type.Put)
.setValue(Bytes.toBytes(state.name()))
.build());
}
/**
* Remove state for table from meta
* (Copied from MetaTableAccessor)
* @param connection to use for deletion
* @param table to delete state for
*/
public static void deleteTableState(Connection connection, TableName table)
throws IOException {
long time = EnvironmentEdgeManager.currentTime();
Delete delete = new Delete(table.getName());
delete.addColumns(TABLE_FAMILY, HConstants.TABLE_STATE_QUALIFIER, time);
deleteFromMetaTable(connection, delete);
LOG.info("Deleted table " + table + " state from META");
}
/**
* Updates state in META
* @param conn connection to use
* @param tableName table to look for
*/
public static void updateTableState(Connection conn, TableName tableName,
TableState.State actual) throws IOException {
Put put = makePutFromTableState(new TableState(tableName, actual),
EnvironmentEdgeManager.currentTime());
conn.getTable(TableName.META_TABLE_NAME).put(put);
}
/**
* Returns the RegionInfo object from the column {@link HConstants#CATALOG_FAMILY} and
* <code>qualifier</code> of the catalog table result.
* (Copied from MetaTableAccessor)
* @param r a Result object from the catalog table scan
* @param qualifier Column family qualifier
* @return An RegionInfo instance or null.
*/
public static RegionInfo getRegionInfo(final Result r, byte [] qualifier) {
Cell cell = r.getColumnLatestCell(CATALOG_FAMILY, qualifier);
if (cell == null) {
return null;
}
return RegionInfo.parseFromOrNull(cell.getValueArray(),
cell.getValueOffset(), cell.getValueLength());
}
/**
* Returns the HRegionLocation parsed from the given meta row Result
* for the given regionInfo and replicaId. The regionInfo can be the default region info
* for the replica.
* (Copied from MetaTableAccessor)
* @param r the meta row result
* @param regionInfo RegionInfo for default replica
* @param replicaId the replicaId for the HRegionLocation
* @return HRegionLocation parsed from the given meta row Result for the given replicaId
*/
private static HRegionLocation getRegionLocation(final Result r, final RegionInfo regionInfo,
final int replicaId) {
ServerName serverName = getServerName(r, replicaId);
long seqNum = getSeqNumDuringOpen(r, replicaId);
RegionInfo replicaInfo = RegionReplicaUtil.getRegionInfoForReplica(regionInfo, replicaId);
return new HRegionLocation(replicaInfo, serverName, seqNum);
}
/**
* The latest seqnum that the server writing to meta observed when opening the region.
* E.g. the seqNum when the result of {@link #getServerName(Result, int)} was written.
* (Copied from MetaTableAccessor)
* @param r Result to pull the seqNum from
* @return SeqNum, or HConstants.NO_SEQNUM if there's no value written.
*/
private static long getSeqNumDuringOpen(final Result r, final int replicaId) {
Cell cell = r.getColumnLatestCell(CATALOG_FAMILY, getSeqNumColumn(replicaId));
if (cell == null || cell.getValueLength() == 0) {
return HConstants.NO_SEQNUM;
}
return Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
}
/**
* Returns a {@link ServerName} from catalog table {@link Result}.
* (Copied from MetaTableAccessor)
* @param r Result to pull from
* @return A ServerName instance or null if necessary fields not found or empty.
*/
@InterfaceAudience.Private // for use by HMaster#getTableRegionRow which is used for testing only
public static ServerName getServerName(final Result r, final int replicaId) {
byte[] serverColumn = getServerColumn(replicaId);
Cell cell = r.getColumnLatestCell(CATALOG_FAMILY, serverColumn);
if (cell == null || cell.getValueLength() == 0) {
return null;
}
String hostAndPort = Bytes.toString(
cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
byte[] startcodeColumn = getStartCodeColumn(replicaId);
cell = r.getColumnLatestCell(CATALOG_FAMILY, startcodeColumn);
if (cell == null || cell.getValueLength() == 0) {
return null;
}
try {
return ServerName.valueOf(hostAndPort,
Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
} catch (IllegalArgumentException e) {
LOG.error("Ignoring invalid region for server " + hostAndPort + "; cell=" + cell, e);
return null;
}
}
/**
* Returns an HRegionLocationList extracted from the result.
* (Copied from MetaTableAccessor)
* @return an HRegionLocationList containing all locations for the region range or null if
* we can't deserialize the result.
*/
public static RegionLocations getRegionLocations(final Result r) {
if (r == null) {
return null;
}
RegionInfo regionInfo = getRegionInfo(r, REGIONINFO_QUALIFIER);
if (regionInfo == null) {
return null;
}
List<HRegionLocation> locations = new ArrayList<>(1);
NavigableMap<byte[], NavigableMap<byte[],byte[]>> familyMap = r.getNoVersionMap();
locations.add(getRegionLocation(r, regionInfo, 0));
NavigableMap<byte[], byte[]> infoMap = familyMap.get(CATALOG_FAMILY);
if (infoMap == null) {
return new RegionLocations(locations);
}
// iterate until all serverName columns are seen
int replicaId = 0;
byte[] serverColumn = getServerColumn(replicaId);
SortedMap<byte[], byte[]> serverMap;
serverMap = infoMap.tailMap(serverColumn, false);
if (serverMap.isEmpty()) {
return new RegionLocations(locations);
}
for (Map.Entry<byte[], byte[]> entry : serverMap.entrySet()) {
replicaId = parseReplicaIdFromServerColumn(entry.getKey());
if (replicaId < 0) {
break;
}
HRegionLocation location = getRegionLocation(r, regionInfo, replicaId);
// In case the region replica is newly created, it's location might be null. We usually do not
// have HRL's in RegionLocations object with null ServerName. They are handled as null HRLs.
if (location.getServerName() == null) {
locations.add(null);
} else {
locations.add(location);
}
}
return new RegionLocations(locations);
}
/**
* Returns the daughter regions by reading the corresponding columns of the catalog table
* Result.
* (Copied from MetaTableAccessor)
* @param data a Result object from the catalog table scan
* @return pair of RegionInfo or PairOfSameType(null, null) if region is not a split parent
*/
public static PairOfSameType<RegionInfo> getDaughterRegions(Result data) {
RegionInfo splitA = getRegionInfo(data, HConstants.SPLITA_QUALIFIER);
RegionInfo splitB = getRegionInfo(data, HConstants.SPLITB_QUALIFIER);
return new PairOfSameType<>(splitA, splitB);
}
/**
* Returns the column qualifier for serialized region state
* (Copied from MetaTableAccessor)
* @param replicaId the replicaId of the region
* @return a byte[] for sn column qualifier
*/
@VisibleForTesting
static byte[] getServerNameColumn(int replicaId) {
return replicaId == 0 ? HConstants.SERVERNAME_QUALIFIER
: Bytes.toBytes(HConstants.SERVERNAME_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
+ String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
}
/**
* Returns the column qualifier for server column for replicaId
* (Copied from MetaTableAccessor)
* @param replicaId the replicaId of the region
* @return a byte[] for server column qualifier
*/
@VisibleForTesting
public static byte[] getServerColumn(int replicaId) {
return replicaId == 0
? HConstants.SERVER_QUALIFIER
: Bytes.toBytes(HConstants.SERVER_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
+ String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
}
/**
* Returns the column qualifier for server start code column for replicaId
* (Copied from MetaTableAccessor)
* @param replicaId the replicaId of the region
* @return a byte[] for server start code column qualifier
*/
@VisibleForTesting
public static byte[] getStartCodeColumn(int replicaId) {
return replicaId == 0
? HConstants.STARTCODE_QUALIFIER
: Bytes.toBytes(HConstants.STARTCODE_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
+ String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
}
/**
* Returns the column qualifier for seqNum column for replicaId
* (Copied from MetaTableAccessor)
* @param replicaId the replicaId of the region
* @return a byte[] for seqNum column qualifier
*/
@VisibleForTesting
public static byte[] getSeqNumColumn(int replicaId) {
return replicaId == 0
? HConstants.SEQNUM_QUALIFIER
: Bytes.toBytes(HConstants.SEQNUM_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
+ String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
}
/**
* Parses the replicaId from the server column qualifier. See top of the class javadoc
* for the actual meta layout
* (Copied from MetaTableAccessor)
* @param serverColumn the column qualifier
* @return an int for the replicaId
*/
@VisibleForTesting
static int parseReplicaIdFromServerColumn(byte[] serverColumn) {
String serverStr = Bytes.toString(serverColumn);
Matcher matcher = SERVER_COLUMN_PATTERN.matcher(serverStr);
if (matcher.matches() && matcher.groupCount() > 0) {
String group = matcher.group(1);
if (group != null && group.length() > 0) {
return Integer.parseInt(group.substring(1), 16);
} else {
return 0;
}
}
return -1;
}
public static class MetaScanner<R> {
public List<R> scanMeta(final Connection conn, Consumer<Scan> scanDecorator,
Function<Result, R> scanProcessor) throws IOException {
final Scan metaScan = new Scan();
scanDecorator.accept(metaScan);
final Table metaTable = conn.getTable(TableName.META_TABLE_NAME);
final ResultScanner rs = metaTable.getScanner(metaScan);
final List<R> results = new ArrayList<>();
Result result;
while((result = rs.next())!=null){
R processedResult = scanProcessor.apply(result);
if(processedResult != null) {
results.add(processedResult);
}
}
return results;
}
}
}