HBASE-23554 Encoded regionname to regionname utility (#923)
Adds shell command regioninfo: hbase(main):001:0> regioninfo '0e6aa5c19ae2b2627649dc7708ce27d0' {ENCODED => 0e6aa5c19ae2b2627649dc7708ce27d0, NAME => 'TestTable,,1575941375972.0e6aa5c19ae2b2627649dc7708ce27d0.', STARTKEY => '', ENDKEY => '00000000000000000000299441'} Took 0.4737 seconds Signed-off-by: Sean Busbey <busbey@apache.org> Signed-off-by: Duo Zhang <zhangduo@apache.org>
This commit is contained in:
parent
0379d5c833
commit
18e3b48eb3
@ -456,8 +456,7 @@ public class HRegionInfo implements RegionInfo, Comparable<HRegionInfo> {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@InterfaceAudience.Private
|
@InterfaceAudience.Private
|
||||||
public static byte [][] parseRegionName(final byte [] regionName)
|
public static byte [][] parseRegionName(final byte [] regionName) throws IOException {
|
||||||
throws IOException {
|
|
||||||
return RegionInfo.parseRegionName(regionName);
|
return RegionInfo.parseRegionName(regionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,6 @@ import org.apache.hadoop.hbase.util.HashKey;
|
|||||||
import org.apache.hadoop.hbase.util.JenkinsHash;
|
import org.apache.hadoop.hbase.util.JenkinsHash;
|
||||||
import org.apache.hadoop.hbase.util.MD5Hash;
|
import org.apache.hadoop.hbase.util.MD5Hash;
|
||||||
import org.apache.hadoop.io.DataInputBuffer;
|
import org.apache.hadoop.io.DataInputBuffer;
|
||||||
import org.apache.hadoop.util.StringUtils;
|
|
||||||
import org.apache.yetus.audience.InterfaceAudience;
|
import org.apache.yetus.audience.InterfaceAudience;
|
||||||
|
|
||||||
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
|
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
|
||||||
@ -346,18 +345,15 @@ public interface RegionInfo {
|
|||||||
return parseRegionName(regionName)[1];
|
return parseRegionName(regionName)[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@InterfaceAudience.Private
|
/**
|
||||||
static boolean isEncodedRegionName(byte[] regionName) throws IOException {
|
* Figure if the passed bytes represent an encoded region name or not.
|
||||||
try {
|
* @param regionName A Region name either encoded or not.
|
||||||
parseRegionName(regionName);
|
* @return True if <code>regionName</code> represents an encoded name.
|
||||||
return false;
|
*/
|
||||||
} catch (IOException e) {
|
@InterfaceAudience.Private // For use by internals only.
|
||||||
if (StringUtils.stringifyException(e)
|
public static boolean isEncodedRegionName(byte[] regionName) throws IOException {
|
||||||
.contains(INVALID_REGION_NAME_FORMAT_MESSAGE)) {
|
// If not parseable as region name, presume encoded. TODO: add stringency; e.g. if hex.
|
||||||
return true;
|
return parseRegionNameOrReturnNull(regionName) == null && regionName.length <= MD5_HEX_LENGTH;
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -596,15 +592,28 @@ public interface RegionInfo {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Separate elements of a regionName.
|
* Separate elements of a regionName.
|
||||||
* @return Array of byte[] containing tableName, startKey and id
|
* @return Array of byte[] containing tableName, startKey and id OR null if
|
||||||
|
* not parseable as a region name.
|
||||||
|
* @throws IOException if not parseable as regionName.
|
||||||
*/
|
*/
|
||||||
static byte [][] parseRegionName(final byte[] regionName)
|
static byte [][] parseRegionName(final byte[] regionName) throws IOException {
|
||||||
throws IOException {
|
byte [][] result = parseRegionNameOrReturnNull(regionName);
|
||||||
// Region name is of the format:
|
if (result == null) {
|
||||||
// tablename,startkey,regionIdTimestamp[_replicaId][.encodedName.]
|
throw new IOException(INVALID_REGION_NAME_FORMAT_MESSAGE + ": " + Bytes.toStringBinary(regionName));
|
||||||
// startkey can contain the delimiter (',') so we parse from the start and end
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// parse from start
|
/**
|
||||||
|
* Separate elements of a regionName.
|
||||||
|
* Region name is of the format:
|
||||||
|
* <code>tablename,startkey,regionIdTimestamp[_replicaId][.encodedName.]</code>.
|
||||||
|
* Startkey can contain the delimiter (',') so we parse from the start and then parse from
|
||||||
|
* the end.
|
||||||
|
* @return Array of byte[] containing tableName, startKey and id OR null if not parseable
|
||||||
|
* as a region name.
|
||||||
|
*/
|
||||||
|
static byte [][] parseRegionNameOrReturnNull(final byte[] regionName) {
|
||||||
int offset = -1;
|
int offset = -1;
|
||||||
for (int i = 0; i < regionName.length; i++) {
|
for (int i = 0; i < regionName.length; i++) {
|
||||||
if (regionName[i] == HConstants.DELIMITER) {
|
if (regionName[i] == HConstants.DELIMITER) {
|
||||||
@ -613,8 +622,7 @@ public interface RegionInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (offset == -1) {
|
if (offset == -1) {
|
||||||
throw new IOException(INVALID_REGION_NAME_FORMAT_MESSAGE
|
return null;
|
||||||
+ ": " + Bytes.toStringBinary(regionName));
|
|
||||||
}
|
}
|
||||||
byte[] tableName = new byte[offset];
|
byte[] tableName = new byte[offset];
|
||||||
System.arraycopy(regionName, 0, tableName, 0, offset);
|
System.arraycopy(regionName, 0, tableName, 0, offset);
|
||||||
@ -622,9 +630,9 @@ public interface RegionInfo {
|
|||||||
|
|
||||||
int endOffset = regionName.length;
|
int endOffset = regionName.length;
|
||||||
// check whether regionName contains encodedName
|
// check whether regionName contains encodedName
|
||||||
if (regionName.length > MD5_HEX_LENGTH + 2
|
if (regionName.length > MD5_HEX_LENGTH + 2 &&
|
||||||
&& regionName[regionName.length-1] == ENC_SEPARATOR
|
regionName[regionName.length-1] == ENC_SEPARATOR &&
|
||||||
&& regionName[regionName.length-MD5_HEX_LENGTH-2] == ENC_SEPARATOR) {
|
regionName[regionName.length-MD5_HEX_LENGTH-2] == ENC_SEPARATOR) {
|
||||||
endOffset = endOffset - MD5_HEX_LENGTH - 2;
|
endOffset = endOffset - MD5_HEX_LENGTH - 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -645,8 +653,7 @@ public interface RegionInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (offset == -1) {
|
if (offset == -1) {
|
||||||
throw new IOException(INVALID_REGION_NAME_FORMAT_MESSAGE
|
return null;
|
||||||
+ ": " + Bytes.toStringBinary(regionName));
|
|
||||||
}
|
}
|
||||||
byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
|
byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
|
||||||
if(offset != tableName.length + 1) {
|
if(offset != tableName.length + 1) {
|
||||||
|
@ -1715,21 +1715,21 @@ public final class ProtobufUtil {
|
|||||||
// Start helpers for Admin
|
// Start helpers for Admin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper to retrieve region info given a region name
|
* A helper to retrieve region info given a region name or an
|
||||||
* using admin protocol.
|
* encoded region name using admin protocol.
|
||||||
*
|
*
|
||||||
* @param admin
|
|
||||||
* @param regionName
|
|
||||||
* @return the retrieved region info
|
* @return the retrieved region info
|
||||||
* @throws IOException
|
|
||||||
*/
|
*/
|
||||||
public static org.apache.hadoop.hbase.client.RegionInfo getRegionInfo(final RpcController controller,
|
public static org.apache.hadoop.hbase.client.RegionInfo getRegionInfo(
|
||||||
final AdminService.BlockingInterface admin, final byte[] regionName) throws IOException {
|
final RpcController controller, final AdminService.BlockingInterface admin,
|
||||||
|
final byte[] regionName) throws IOException {
|
||||||
try {
|
try {
|
||||||
GetRegionInfoRequest request =
|
GetRegionInfoRequest request =
|
||||||
|
org.apache.hadoop.hbase.client.RegionInfo.isEncodedRegionName(regionName)?
|
||||||
|
GetRegionInfoRequest.newBuilder().setRegion(RequestConverter.
|
||||||
|
buildRegionSpecifier(RegionSpecifierType.ENCODED_REGION_NAME, regionName)).build():
|
||||||
RequestConverter.buildGetRegionInfoRequest(regionName);
|
RequestConverter.buildGetRegionInfoRequest(regionName);
|
||||||
GetRegionInfoResponse response =
|
GetRegionInfoResponse response = admin.getRegionInfo(controller, request);
|
||||||
admin.getRegionInfo(controller, request);
|
|
||||||
return toRegionInfo(response.getRegionInfo());
|
return toRegionInfo(response.getRegionInfo());
|
||||||
} catch (ServiceException se) {
|
} catch (ServiceException se) {
|
||||||
throw getRemoteException(se);
|
throw getRemoteException(se);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
@ -1773,24 +1772,44 @@ public class MasterRpcServices extends RSRpcServices
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method implements Admin getRegionInfo. On RegionServer, it is
|
||||||
|
* able to return RegionInfo and detail. On Master, it just returns
|
||||||
|
* RegionInfo. On Master it has been hijacked to return Mob detail.
|
||||||
|
* Master implementation is good for querying full region name if
|
||||||
|
* you only have the encoded name (useful around region replicas
|
||||||
|
* for example which do not have a row in hbase:meta).
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
@QosPriority(priority=HConstants.ADMIN_QOS)
|
@QosPriority(priority=HConstants.ADMIN_QOS)
|
||||||
public GetRegionInfoResponse getRegionInfo(final RpcController controller,
|
public GetRegionInfoResponse getRegionInfo(final RpcController controller,
|
||||||
final GetRegionInfoRequest request) throws ServiceException {
|
final GetRegionInfoRequest request) throws ServiceException {
|
||||||
byte[] regionName = request.getRegion().getValue().toByteArray();
|
RegionInfo ri = null;
|
||||||
TableName tableName = RegionInfo.getTable(regionName);
|
try {
|
||||||
if (MobUtils.isMobRegionName(tableName, regionName)) {
|
ri = getRegionInfo(request.getRegion());
|
||||||
// a dummy region info contains the compaction state.
|
} catch(UnknownRegionException ure) {
|
||||||
RegionInfo mobRegionInfo = MobUtils.getMobRegionInfo(tableName);
|
throw new ServiceException(ure);
|
||||||
GetRegionInfoResponse.Builder builder = GetRegionInfoResponse.newBuilder();
|
|
||||||
builder.setRegionInfo(ProtobufUtil.toRegionInfo(mobRegionInfo));
|
|
||||||
if (request.hasCompactionState() && request.getCompactionState()) {
|
|
||||||
builder.setCompactionState(master.getMobCompactionState(tableName));
|
|
||||||
}
|
|
||||||
return builder.build();
|
|
||||||
} else {
|
|
||||||
return super.getRegionInfo(controller, request);
|
|
||||||
}
|
}
|
||||||
|
GetRegionInfoResponse.Builder builder = GetRegionInfoResponse.newBuilder();
|
||||||
|
if (ri != null) {
|
||||||
|
builder.setRegionInfo(ProtobufUtil.toRegionInfo(ri));
|
||||||
|
} else {
|
||||||
|
// Is it a MOB name? These work differently.
|
||||||
|
byte [] regionName = request.getRegion().getValue().toByteArray();
|
||||||
|
TableName tableName = RegionInfo.getTable(regionName);
|
||||||
|
if (MobUtils.isMobRegionName(tableName, regionName)) {
|
||||||
|
// a dummy region info contains the compaction state.
|
||||||
|
RegionInfo mobRegionInfo = MobUtils.getMobRegionInfo(tableName);
|
||||||
|
builder.setRegionInfo(ProtobufUtil.toRegionInfo(mobRegionInfo));
|
||||||
|
if (request.hasCompactionState() && request.getCompactionState()) {
|
||||||
|
builder.setCompactionState(master.getMobCompactionState(tableName));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If unknown RegionInfo and not a MOB region, it is unknown.
|
||||||
|
throw new ServiceException(new UnknownRegionException(Bytes.toString(regionName)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1772,6 +1772,9 @@ public class RSRpcServices implements HBaseRPCErrorHandler,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Master implementation of this Admin Service differs given it is not
|
||||||
|
// able to supply detail only known to RegionServer. See note on
|
||||||
|
// MasterRpcServers#getRegionInfo.
|
||||||
@Override
|
@Override
|
||||||
@QosPriority(priority=HConstants.ADMIN_QOS)
|
@QosPriority(priority=HConstants.ADMIN_QOS)
|
||||||
public GetRegionInfoResponse getRegionInfo(final RpcController controller,
|
public GetRegionInfoResponse getRegionInfo(final RpcController controller,
|
||||||
|
@ -749,6 +749,34 @@ public class TestAdmin2 extends TestAdminBase {
|
|||||||
// Make sure that the store size is still the actual file system's store size.
|
// Make sure that the store size is still the actual file system's store size.
|
||||||
Assert.assertEquals(expectedStoreFilesSize, store.getSize());
|
Assert.assertEquals(expectedStoreFilesSize, store.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test querying using the encoded name only. When encoded name passed,
|
||||||
|
// and the target server is the Master, we return the full region name.
|
||||||
|
// Convenience.
|
||||||
|
testGetWithEncodedRegionName(conn, region.getRegionInfo());
|
||||||
|
testGetWithRegionName(conn, region.getRegionInfo());
|
||||||
|
// Try querying meta encoded name.
|
||||||
|
testGetWithEncodedRegionName(conn, RegionInfoBuilder.FIRST_META_REGIONINFO);
|
||||||
|
testGetWithRegionName(conn, RegionInfoBuilder.FIRST_META_REGIONINFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do get of RegionInfo from Master using encoded region name.
|
||||||
|
*/
|
||||||
|
private void testGetWithEncodedRegionName(ClusterConnection conn, RegionInfo inputRI)
|
||||||
|
throws IOException {
|
||||||
|
RegionInfo ri = ProtobufUtil.getRegionInfo(null,
|
||||||
|
conn.getAdmin(TEST_UTIL.getMiniHBaseCluster().getMaster().getServerName()),
|
||||||
|
inputRI.getEncodedNameAsBytes());
|
||||||
|
assertEquals(inputRI, ri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testGetWithRegionName(ClusterConnection conn, RegionInfo inputRI)
|
||||||
|
throws IOException {
|
||||||
|
RegionInfo ri = ProtobufUtil.getRegionInfo(null,
|
||||||
|
conn.getAdmin(TEST_UTIL.getMiniHBaseCluster().getMaster().getServerName()),
|
||||||
|
inputRI.getRegionName());
|
||||||
|
assertEquals(inputRI, ri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -364,6 +364,7 @@ Shell.load_command_group(
|
|||||||
clear_block_cache
|
clear_block_cache
|
||||||
stop_master
|
stop_master
|
||||||
stop_regionserver
|
stop_regionserver
|
||||||
|
regioninfo
|
||||||
rit
|
rit
|
||||||
list_decommissioned_regionservers
|
list_decommissioned_regionservers
|
||||||
decommission_regionservers
|
decommission_regionservers
|
||||||
|
47
hbase-shell/src/main/ruby/shell/commands/regioninfo.rb
Normal file
47
hbase-shell/src/main/ruby/shell/commands/regioninfo.rb
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
module Shell
|
||||||
|
module Commands
|
||||||
|
class Regioninfo < Command
|
||||||
|
def help
|
||||||
|
<<-EOF
|
||||||
|
Return RegionInfo. Takes Region name or an encoded Region name
|
||||||
|
(Of use when all you have is an encoded Region name).
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
Below we pass first encoded region name and then full region name.
|
||||||
|
|
||||||
|
hbase(main):002:0> regioninfo '1588230740'
|
||||||
|
{ENCODED => 1588230740, NAME => 'hbase:meta,,1', STARTKEY => '', ENDKEY => ''}
|
||||||
|
hbase(main):002:0> regioninfo 'hbase:meta,,1'
|
||||||
|
{ENCODED => 1588230740, NAME => 'hbase:meta,,1', STARTKEY => '', ENDKEY => ''}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
|
||||||
|
def command(regionname)
|
||||||
|
connection = org.apache.hadoop.hbase.client.ConnectionFactory.createConnection()
|
||||||
|
admin = connection.getAdmin()
|
||||||
|
sn = servername != nil ? ServerName.valueOf(servername): admin.getMaster()
|
||||||
|
puts org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil.getRegionInfo(nil,
|
||||||
|
connection.getAdmin(sn), regionname.to_java_bytes)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user