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:
Sakthi 2020-02-13 14:40:46 -08:00 committed by GitHub
parent 533302adde
commit 5f61df4792
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 112 additions and 27 deletions

View File

@ -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

View File

@ -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

View File

@ -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
#---------------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------------

View File

@ -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)

View File

@ -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

View File

@ -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