HBASE-22827 Expose multi-region merge in shell and Admin API (#1138)
mergeRegionsAsync admin API with two regions as parameters deprecated since 2.3.0 and removed from 4.0 merge_region shell command now supports multiple regions merge since 2.3.0 & 3.0.0 Signed-off-by: Viraj Jasani <vjasani@apache.org> Signed-off-by: Esteban Gutierrez <esteban@apache.org> Signed-off-by: Josh Elser <elserj@apache.org>
This commit is contained in:
parent
533302adde
commit
5f61df4792
|
@ -906,7 +906,10 @@ public interface Admin extends Abortable, Closeable {
|
||||||
* @param forcible <code>true</code> if do a compulsory merge, otherwise we will only merge two
|
* @param forcible <code>true</code> if do a compulsory merge, otherwise we will only merge two
|
||||||
* adjacent regions
|
* adjacent regions
|
||||||
* @throws IOException if a remote or network exception occurs
|
* @throws IOException if a remote or network exception occurs
|
||||||
|
* @deprecated since 2.3.0 and will be removed in 4.0.0. Multi-region merge feature is now
|
||||||
|
* supported. Use {@link #mergeRegionsAsync(byte[][], boolean)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
default Future<Void> mergeRegionsAsync(byte[] nameOfRegionA, byte[] nameOfRegionB,
|
default Future<Void> mergeRegionsAsync(byte[] nameOfRegionA, byte[] nameOfRegionB,
|
||||||
boolean forcible) throws IOException {
|
boolean forcible) throws IOException {
|
||||||
byte[][] nameofRegionsToMerge = new byte[2][];
|
byte[][] nameofRegionsToMerge = new byte[2][];
|
||||||
|
@ -916,11 +919,7 @@ public interface Admin extends Abortable, Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge regions. Asynchronous operation.
|
* Merge multiple regions (>=2). Asynchronous operation.
|
||||||
* <p/>
|
|
||||||
* You may get a {@code DoNotRetryIOException} if you pass more than two regions in but the master
|
|
||||||
* does not support merging more than two regions. At least till 2.2.0, we still only support
|
|
||||||
* merging two regions.
|
|
||||||
* @param nameofRegionsToMerge encoded or full name of daughter regions
|
* @param nameofRegionsToMerge encoded or full name of daughter regions
|
||||||
* @param forcible <code>true</code> if do a compulsory merge, otherwise we will only merge
|
* @param forcible <code>true</code> if do a compulsory merge, otherwise we will only merge
|
||||||
* adjacent regions
|
* adjacent regions
|
||||||
|
|
|
@ -513,18 +513,17 @@ public interface AsyncAdmin {
|
||||||
* @param nameOfRegionB encoded or full name of region b
|
* @param nameOfRegionB encoded or full name of region b
|
||||||
* @param forcible true if do a compulsory merge, otherwise we will only merge two adjacent
|
* @param forcible true if do a compulsory merge, otherwise we will only merge two adjacent
|
||||||
* regions
|
* regions
|
||||||
|
* @deprecated since 2.3.0 and will be removed in 4.0.0.Use {@link #mergeRegions(List, boolean)}
|
||||||
|
* instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
default CompletableFuture<Void> mergeRegions(byte[] nameOfRegionA, byte[] nameOfRegionB,
|
default CompletableFuture<Void> mergeRegions(byte[] nameOfRegionA, byte[] nameOfRegionB,
|
||||||
boolean forcible) {
|
boolean forcible) {
|
||||||
return mergeRegions(Arrays.asList(nameOfRegionA, nameOfRegionB), forcible);
|
return mergeRegions(Arrays.asList(nameOfRegionA, nameOfRegionB), forcible);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge regions.
|
* Merge multiple regions (>=2).
|
||||||
* <p/>
|
|
||||||
* You may get a {@code DoNotRetryIOException} if you pass more than two regions in but the master
|
|
||||||
* does not support merging more than two regions. At least till 2.2.0, we still only support
|
|
||||||
* merging two regions.
|
|
||||||
* @param nameOfRegionsToMerge encoded or full name of daughter regions
|
* @param nameOfRegionsToMerge encoded or full name of daughter regions
|
||||||
* @param forcible true if do a compulsory merge, otherwise we will only merge two adjacent
|
* @param forcible true if do a compulsory merge, otherwise we will only merge two adjacent
|
||||||
* regions
|
* regions
|
||||||
|
|
|
@ -535,14 +535,29 @@ module Hbase
|
||||||
end
|
end
|
||||||
|
|
||||||
#----------------------------------------------------------------------------------------------
|
#----------------------------------------------------------------------------------------------
|
||||||
# Merge two regions
|
# Merge multiple regions
|
||||||
def merge_region(region_a_name, region_b_name, force)
|
def merge_region(regions, force)
|
||||||
|
unless regions.is_a?(Array)
|
||||||
|
raise(ArgumentError, "Type of #{regions.inspect} is #{regions.class}, but expected Array")
|
||||||
|
end
|
||||||
|
region_array = Java::byte[][regions.length].new
|
||||||
|
i = 0
|
||||||
|
while i < regions.length
|
||||||
|
unless regions[i].is_a?(String)
|
||||||
|
raise(
|
||||||
|
ArgumentError,
|
||||||
|
"Type of #{regions[i].inspect} is #{regions[i].class}, but expected String"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
region_array[i] = regions[i].to_java_bytes
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
org.apache.hadoop.hbase.util.FutureUtils.get(
|
||||||
@admin.mergeRegionsAsync(
|
@admin.mergeRegionsAsync(
|
||||||
region_a_name.to_java_bytes,
|
region_array,
|
||||||
region_b_name.to_java_bytes,
|
|
||||||
java.lang.Boolean.valueOf(force)
|
java.lang.Boolean.valueOf(force)
|
||||||
)
|
)
|
||||||
return nil
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
#----------------------------------------------------------------------------------------------
|
#----------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -137,7 +137,11 @@ module Shell
|
||||||
raise "Table #{cause.message} should be disabled!"
|
raise "Table #{cause.message} should be disabled!"
|
||||||
end
|
end
|
||||||
if cause.is_a?(org.apache.hadoop.hbase.UnknownRegionException)
|
if cause.is_a?(org.apache.hadoop.hbase.UnknownRegionException)
|
||||||
raise "Unknown region #{args.first}!"
|
raise cause.message
|
||||||
|
end
|
||||||
|
if cause.is_a?(org.apache.hadoop.hbase.exceptions.MergeRegionException)
|
||||||
|
strs = cause.message.split("\n")
|
||||||
|
raise(strs[0]).to_s unless strs.empty?
|
||||||
end
|
end
|
||||||
if cause.is_a?(org.apache.hadoop.hbase.NamespaceNotFoundException)
|
if cause.is_a?(org.apache.hadoop.hbase.NamespaceNotFoundException)
|
||||||
s = /.*NamespaceNotFoundException: (?<namespace>[^\n]+).*/.match(cause.message)
|
s = /.*NamespaceNotFoundException: (?<namespace>[^\n]+).*/.match(cause.message)
|
||||||
|
|
|
@ -22,7 +22,7 @@ module Shell
|
||||||
class MergeRegion < Command
|
class MergeRegion < Command
|
||||||
def help
|
def help
|
||||||
<<-EOF
|
<<-EOF
|
||||||
Merge two regions. Passing 'true' as the optional third parameter will force
|
Merge multiple (2 or more) regions. Passing 'true' as the optional third parameter will force
|
||||||
a merge ('force' merges regardless else merge will fail unless passed
|
a merge ('force' merges regardless else merge will fail unless passed
|
||||||
adjacent regions. 'force' is for expert use only).
|
adjacent regions. 'force' is for expert use only).
|
||||||
|
|
||||||
|
@ -31,18 +31,42 @@ region name is the hash suffix on region names: e.g. if the region name were
|
||||||
TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396. then
|
TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396. then
|
||||||
the encoded region name portion is 527db22f95c8a9e0116f0cc13c680396
|
the encoded region name portion is 527db22f95c8a9e0116f0cc13c680396
|
||||||
|
|
||||||
|
You can either pass the list of regions as comma separated values or as an
|
||||||
|
array of regions as shown:
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
hbase> merge_region 'FULL_REGIONNAME', 'FULL_REGIONNAME'
|
hbase> merge_region 'FULL_REGIONNAME', 'FULL_REGIONNAME'
|
||||||
hbase> merge_region 'FULL_REGIONNAME', 'FULL_REGIONNAME', true
|
hbase> merge_region 'FULL_REGIONNAME', 'FULL_REGIONNAME', 'FULL_REGIONNAME', ...
|
||||||
|
hbase> merge_region 'FULL_REGIONNAME', 'FULL_REGIONNAME', 'FULL_REGIONNAME', ..., true
|
||||||
|
|
||||||
|
hbase> merge_region ['FULL_REGIONNAME', 'FULL_REGIONNAME']
|
||||||
|
hbase> merge_region ['FULL_REGIONNAME', 'FULL_REGIONNAME', 'FULL_REGIONNAME', ...]
|
||||||
|
hbase> merge_region ['FULL_REGIONNAME', 'FULL_REGIONNAME', 'FULL_REGIONNAME', ...], true
|
||||||
|
|
||||||
hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME'
|
hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME'
|
||||||
hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', true
|
hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', ...
|
||||||
|
hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', ..., true
|
||||||
|
|
||||||
|
hbase> merge_region ['ENCODED_REGIONNAME', 'ENCODED_REGIONNAME']
|
||||||
|
hbase> merge_region ['ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', ...]
|
||||||
|
hbase> merge_region ['ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', ...], true
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
|
|
||||||
def command(region_a_name, region_b_name, force = 'false')
|
def command(*args)
|
||||||
admin.merge_region(region_a_name, region_b_name, force)
|
args = args.flatten.compact
|
||||||
|
args_len = args.length
|
||||||
|
raise(ArgumentError, 'Must pass at least 2 regions to merge') unless args_len > 1
|
||||||
|
force = false
|
||||||
|
if(args_len > 2)
|
||||||
|
last = args[args_len-1]
|
||||||
|
if [true, false].include? last
|
||||||
|
force = last
|
||||||
|
args = args[0...-1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
admin.merge_region(args, force)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -595,15 +595,59 @@ module Hbase
|
||||||
command(:list_regions, @test_name)
|
command(:list_regions, @test_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
define_test 'merge two regions' do
|
define_test 'merge regions' do
|
||||||
@t_name = 'hbase_shell_merge'
|
@t_name = 'hbase_shell_merge'
|
||||||
|
@t_name2 = 'hbase_shell_merge_2'
|
||||||
drop_test_table(@t_name)
|
drop_test_table(@t_name)
|
||||||
|
drop_test_table(@t_name2)
|
||||||
admin.create(@t_name, 'a', NUMREGIONS => 10, SPLITALGO => 'HexStringSplit')
|
admin.create(@t_name, 'a', NUMREGIONS => 10, SPLITALGO => 'HexStringSplit')
|
||||||
r1 = command(:locate_region, @t_name, '')
|
r1 = command(:locate_region, @t_name, '1')
|
||||||
r2 = command(:locate_region, @t_name, '1')
|
r2 = command(:locate_region, @t_name, '2')
|
||||||
|
r3 = command(:locate_region, @t_name, '4')
|
||||||
|
r4 = command(:locate_region, @t_name, '5')
|
||||||
|
r5 = command(:locate_region, @t_name, '7')
|
||||||
|
r6 = command(:locate_region, @t_name, '8')
|
||||||
region1 = r1.getRegion.getRegionNameAsString
|
region1 = r1.getRegion.getRegionNameAsString
|
||||||
region2 = r2.getRegion.getRegionNameAsString
|
region2 = r2.getRegion.getRegionNameAsString
|
||||||
command(:merge_region, region1, region2, true)
|
region3 = r3.getRegion.getRegionNameAsString
|
||||||
|
region4 = r4.getRegion.getRegionNameAsString
|
||||||
|
region5 = r5.getRegion.getRegionNameAsString
|
||||||
|
region6 = r6.getRegion.getRegionNameAsString
|
||||||
|
# only 1 region
|
||||||
|
assert_raise(ArgumentError) do
|
||||||
|
command(:merge_region, 'a')
|
||||||
|
end
|
||||||
|
# only 1 region with force=true
|
||||||
|
assert_raise(ArgumentError) do
|
||||||
|
command(:merge_region, 'a', true)
|
||||||
|
end
|
||||||
|
# non-existing region
|
||||||
|
assert_raise(RuntimeError) do
|
||||||
|
command(:merge_region, 'a','b')
|
||||||
|
end
|
||||||
|
# duplicate regions
|
||||||
|
assert_raise(RuntimeError) do
|
||||||
|
command(:merge_region, region1,region1,region1)
|
||||||
|
end
|
||||||
|
# 3 non-adjacent regions without forcible=true
|
||||||
|
assert_raise(RuntimeError) do
|
||||||
|
command(:merge_region, region1,region2,region4)
|
||||||
|
end
|
||||||
|
# 2 adjacent regions
|
||||||
|
command(:merge_region, region1,region2)
|
||||||
|
# 3 non-adjacent regions with forcible=true
|
||||||
|
command(:merge_region, region3,region5,region6, true)
|
||||||
|
|
||||||
|
admin.create(@t_name2, 'a', NUMREGIONS => 5, SPLITALGO => 'HexStringSplit')
|
||||||
|
r1 = command(:locate_region, @t_name2, '1')
|
||||||
|
r2 = command(:locate_region, @t_name2, '4')
|
||||||
|
r3 = command(:locate_region, @t_name2, '7')
|
||||||
|
region1 = r1.getRegion.getRegionNameAsString
|
||||||
|
region2 = r2.getRegion.getRegionNameAsString
|
||||||
|
region3 = r3.getRegion.getRegionNameAsString
|
||||||
|
|
||||||
|
# accept array of regions
|
||||||
|
command(:merge_region, [region1,region2,region3])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue