You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by sa...@apache.org on 2020/03/02 09:14:26 UTC

[hbase] branch branch-2.1 updated: HBASE-22827 Expose multi-region merge in shell and Admin API (#1218)

This is an automated email from the ASF dual-hosted git repository.

sakthi pushed a commit to branch branch-2.1
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/branch-2.1 by this push:
     new 5b3e59e  HBASE-22827 Expose multi-region merge in shell and Admin API (#1218)
5b3e59e is described below

commit 5b3e59e2347630f9a968e109835609fee77161c3
Author: Sakthi <sa...@apache.org>
AuthorDate: Mon Mar 2 01:14:16 2020 -0800

    HBASE-22827 Expose multi-region merge in shell and Admin API (#1218)
    
    (cherry picked from commit c4a03c14ecc50604f44cf3b97ffdc40eddda70fd)
    
    Signed-off-by: Viraj Jasani <vj...@apache.org>
---
 .../java/org/apache/hadoop/hbase/client/Admin.java |  6 +--
 .../org/apache/hadoop/hbase/client/AsyncAdmin.java |  6 +--
 hbase-shell/src/main/ruby/hbase/admin.rb           | 28 +++++++++--
 hbase-shell/src/main/ruby/shell/commands.rb        |  6 ++-
 .../src/main/ruby/shell/commands/merge_region.rb   | 34 +++++++++++--
 hbase-shell/src/test/ruby/hbase/admin_test.rb      | 55 ++++++++++++++++++++++
 6 files changed, 114 insertions(+), 21 deletions(-)

diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
index 221935a..72cb884 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java
@@ -1296,11 +1296,7 @@ public interface Admin extends Abortable, Closeable {
   }
 
   /**
-   * Merge regions. 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.1.10, we still only support
-   * merging two regions.
+   * Merge multiple regions (>=2). Asynchronous operation.
    * @param nameofRegionsToMerge encoded or full name of daughter regions
    * @param forcible <code>true</code> if do a compulsory merge, otherwise we will only merge
    *          adjacent regions
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
index 31e96e0..cd67d5a 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java
@@ -481,11 +481,7 @@ public interface AsyncAdmin {
   }
 
   /**
-   * Merge regions.
-   * <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.1.10, we still only support
-   * merging two regions.
+   * Merge multiple regions (>=2).
    * @param nameOfRegionsToMerge encoded or full name of daughter regions
    * @param forcible true if do a compulsory merge, otherwise we will only merge two adjacent
    *          regions
diff --git a/hbase-shell/src/main/ruby/hbase/admin.rb b/hbase-shell/src/main/ruby/hbase/admin.rb
index 8709b23..1744f5c 100644
--- a/hbase-shell/src/main/ruby/hbase/admin.rb
+++ b/hbase-shell/src/main/ruby/hbase/admin.rb
@@ -501,11 +501,29 @@ module Hbase
     end
 
     #----------------------------------------------------------------------------------------------
-    # Merge two regions
-    def merge_region(region_a_name, region_b_name, force)
-      @admin.mergeRegions(region_a_name.to_java_bytes,
-                          region_b_name.to_java_bytes,
-                          java.lang.Boolean.valueOf(force))
+    # Merge multiple regions
+    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(
+              region_array,
+              java.lang.Boolean.valueOf(force)
+          )
+      )
     end
 
     #----------------------------------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands.rb b/hbase-shell/src/main/ruby/shell/commands.rb
index d60d07c..731b340 100644
--- a/hbase-shell/src/main/ruby/shell/commands.rb
+++ b/hbase-shell/src/main/ruby/shell/commands.rb
@@ -137,7 +137,11 @@ module Shell
           raise "Table #{cause.message} should be disabled!"
         end
         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
         if cause.is_a?(org.apache.hadoop.hbase.NamespaceNotFoundException)
           s = /.*NamespaceNotFoundException: (?<namespace>[^\n]+).*/.match(cause.message)
diff --git a/hbase-shell/src/main/ruby/shell/commands/merge_region.rb b/hbase-shell/src/main/ruby/shell/commands/merge_region.rb
index b4f6cae..ed17236 100644
--- a/hbase-shell/src/main/ruby/shell/commands/merge_region.rb
+++ b/hbase-shell/src/main/ruby/shell/commands/merge_region.rb
@@ -22,7 +22,7 @@ module Shell
     class MergeRegion < Command
       def help
         <<-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
 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
 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:
 
   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', 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
       end
 
-      def command(region_a_name, region_b_name, force = 'false')
-        admin.merge_region(region_a_name, region_b_name, force)
+      def command(*args)
+        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
diff --git a/hbase-shell/src/test/ruby/hbase/admin_test.rb b/hbase-shell/src/test/ruby/hbase/admin_test.rb
index 9ade5f6..197c033 100644
--- a/hbase-shell/src/test/ruby/hbase/admin_test.rb
+++ b/hbase-shell/src/test/ruby/hbase/admin_test.rb
@@ -487,6 +487,61 @@ module Hbase
     define_test "list regions should allow table name" do
       command(:list_regions, @test_name)
     end
+
+    define_test 'merge regions' do
+      @t_name = 'hbase_shell_merge'
+      @t_name2 = 'hbase_shell_merge_2'
+      drop_test_table(@t_name)
+      drop_test_table(@t_name2)
+      admin.create(@t_name, 'a', NUMREGIONS => 10, SPLITALGO => 'HexStringSplit')
+      r1 = 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
+      region2 = r2.getRegion.getRegionNameAsString
+      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
 
   # Simple administration methods tests