You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by zg...@apache.org on 2020/06/24 10:46:26 UTC

[hbase] branch master updated: HBASE-24431 RSGroupInfo add configuration map to store something extra (#1782)

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

zghao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/master by this push:
     new 4b89ee2  HBASE-24431 RSGroupInfo add configuration map to store something extra (#1782)
4b89ee2 is described below

commit 4b89ee24f33d6dc268a6910ea497e9d0a98a14a9
Author: XinSun <dd...@gmail.com>
AuthorDate: Wed Jun 24 18:46:09 2020 +0800

    HBASE-24431 RSGroupInfo add configuration map to store something extra (#1782)
    
    Signed-off-by: Duo Zhang <zh...@apache.org>
    Signed-off-by: Nick Dimiduk <nd...@apache.org>
    Signed-off-by: Guanghao Zhang <zg...@apache.org>
---
 .../java/org/apache/hadoop/hbase/client/Admin.java |  8 ++
 .../hadoop/hbase/client/AdminOverAsyncAdmin.java   |  6 ++
 .../org/apache/hadoop/hbase/client/AsyncAdmin.java |  8 ++
 .../hadoop/hbase/client/AsyncHBaseAdmin.java       |  6 ++
 .../hadoop/hbase/client/RawAsyncHBaseAdmin.java    | 21 +++++
 .../hadoop/hbase/shaded/protobuf/ProtobufUtil.java | 16 ++--
 .../apache/hadoop/hbase/rsgroup/RSGroupInfo.java   | 32 +++++++
 .../src/main/protobuf/server/master/Master.proto   |  3 +
 .../src/main/protobuf/server/rsgroup/RSGroup.proto |  1 +
 .../protobuf/server/rsgroup/RSGroupAdmin.proto     |  8 ++
 .../hadoop/hbase/coprocessor/MasterObserver.java   | 21 +++++
 .../hadoop/hbase/master/MasterCoprocessorHost.java | 21 +++++
 .../hadoop/hbase/master/MasterRpcServices.java     | 26 ++++++
 .../hbase/rsgroup/DisabledRSGroupInfoManager.java  |  6 ++
 .../hadoop/hbase/rsgroup/RSGroupInfoManager.java   |  9 ++
 .../hbase/rsgroup/RSGroupInfoManagerImpl.java      |  8 ++
 .../hadoop/hbase/rsgroup/TestRSGroupConfig.java    | 99 ++++++++++++++++++++++
 .../hbase/rsgroup/VerifyingRSGroupAdmin.java       |  7 ++
 hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb   | 37 ++++++++
 hbase-shell/src/main/ruby/shell.rb                 |  2 +
 .../ruby/shell/commands/alter_rsgroup_config.rb    | 38 +++++++++
 .../ruby/shell/commands/show_rsgroup_config.rb     | 43 ++++++++++
 .../src/test/ruby/shell/rsgroup_shell_test.rb      | 14 +++
 .../hadoop/hbase/thrift2/client/ThriftAdmin.java   |  6 ++
 24 files changed, 441 insertions(+), 5 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 c82b101..520a8fc 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
@@ -2428,4 +2428,12 @@ public interface Admin extends Abortable, Closeable {
    */
   void renameRSGroup(String oldName, String newName) throws IOException;
 
+  /**
+   * Update RSGroup configuration
+   * @param groupName the group name
+   * @param configuration new configuration of the group name to be set
+   * @throws IOException if a remote or network exception occurs
+   */
+  void updateRSGroupConfig(String groupName, Map<String, String> configuration) throws IOException;
+
 }
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java
index 8f2d288..50dce89 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AdminOverAsyncAdmin.java
@@ -1041,4 +1041,10 @@ class AdminOverAsyncAdmin implements Admin {
   public void renameRSGroup(String oldName, String newName) throws IOException {
     get(admin.renameRSGroup(oldName, newName));
   }
+
+  @Override
+  public void updateRSGroupConfig(String groupName, Map<String, String> configuration)
+      throws IOException {
+    get(admin.updateRSGroupConfig(groupName, configuration));
+  }
 }
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 c6ef55c..10f2b8a 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
@@ -1639,4 +1639,12 @@ public interface AsyncAdmin {
    * @throws IOException if a remote or network exception occurs
    */
   CompletableFuture<Void> renameRSGroup(String oldName, String newName);
+
+  /**
+   * Update RSGroup configuration
+   * @param groupName the group name
+   * @param configuration new configuration of the group name to be set
+   * @throws IOException if a remote or network exception occurs
+   */
+  CompletableFuture<Void> updateRSGroupConfig(String groupName, Map<String, String> configuration);
 }
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
index 8b29ac9..704359f 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java
@@ -918,4 +918,10 @@ class AsyncHBaseAdmin implements AsyncAdmin {
   public CompletableFuture<Void> renameRSGroup(String oldName, String newName) {
     return wrap(rawAdmin.renameRSGroup(oldName, newName));
   }
+
+  @Override
+  public CompletableFuture<Void>
+    updateRSGroupConfig(String groupName, Map<String, String> configuration) {
+    return wrap(rawAdmin.updateRSGroupConfig(groupName, configuration));
+  }
 }
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java
index 184bbe3..8cc911a 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java
@@ -145,6 +145,7 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.StopServerR
 import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.UpdateConfigurationRequest;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.UpdateConfigurationResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.NameStringPair;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.ProcedureDescription;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.TableSchema;
@@ -311,6 +312,8 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.Remo
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.RemoveServersResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.RenameRSGroupRequest;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.RenameRSGroupResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.UpdateRSGroupConfigRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.UpdateRSGroupConfigResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.ReplicationProtos.AddReplicationPeerRequest;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.ReplicationProtos.AddReplicationPeerResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.ReplicationProtos.DisableReplicationPeerRequest;
@@ -4164,4 +4167,22 @@ class RawAsyncHBaseAdmin implements AsyncAdmin {
         )
       ).call();
   }
+
+  @Override
+  public CompletableFuture<Void>
+    updateRSGroupConfig(String groupName, Map<String, String> configuration) {
+    UpdateRSGroupConfigRequest.Builder request = UpdateRSGroupConfigRequest.newBuilder()
+        .setGroupName(groupName);
+    if (configuration != null) {
+      configuration.entrySet().forEach(e ->
+          request.addConfiguration(NameStringPair.newBuilder().setName(e.getKey())
+              .setValue(e.getValue()).build()));
+    }
+    return this.<Void> newMasterCaller()
+        .action(((controller, stub) ->
+            this.<UpdateRSGroupConfigRequest, UpdateRSGroupConfigResponse, Void> call(
+                controller, stub, request.build(),
+              (s, c, req, done) -> s.updateRSGroupConfig(c, req, done), resp -> null))
+        ).call();
+  }
 }
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java
index 4de8778..4cdbfbc 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java
@@ -3514,14 +3514,16 @@ public final class ProtobufUtil {
   }
 
   public static RSGroupInfo toGroupInfo(RSGroupProtos.RSGroupInfo proto) {
-    RSGroupInfo RSGroupInfo = new RSGroupInfo(proto.getName());
+    RSGroupInfo rsGroupInfo = new RSGroupInfo(proto.getName());
     for (HBaseProtos.ServerName el : proto.getServersList()) {
-      RSGroupInfo.addServer(Address.fromParts(el.getHostName(), el.getPort()));
+      rsGroupInfo.addServer(Address.fromParts(el.getHostName(), el.getPort()));
     }
     for (HBaseProtos.TableName pTableName : proto.getTablesList()) {
-      RSGroupInfo.addTable(ProtobufUtil.toTableName(pTableName));
+      rsGroupInfo.addTable(ProtobufUtil.toTableName(pTableName));
     }
-    return RSGroupInfo;
+    proto.getConfigurationList().forEach(pair ->
+        rsGroupInfo.setConfiguration(pair.getName(), pair.getValue()));
+    return rsGroupInfo;
   }
 
   public static RSGroupProtos.RSGroupInfo toProtoGroupInfo(RSGroupInfo pojo) {
@@ -3534,7 +3536,11 @@ public final class ProtobufUtil {
       hostports.add(HBaseProtos.ServerName.newBuilder().setHostName(el.getHostname())
           .setPort(el.getPort()).build());
     }
+    List<NameStringPair> configuration = pojo.getConfiguration().entrySet()
+        .stream().map(entry -> NameStringPair.newBuilder()
+            .setName(entry.getKey()).setValue(entry.getValue()).build())
+        .collect(Collectors.toList());
     return RSGroupProtos.RSGroupInfo.newBuilder().setName(pojo.getName()).addAllServers(hostports)
-        .addAllTables(tables).build();
+        .addAllTables(tables).addAllConfiguration(configuration).build();
   }
 }
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfo.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfo.java
index 5d70d1b..616e6f7 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfo.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfo.java
@@ -19,6 +19,10 @@
 package org.apache.hadoop.hbase.rsgroup;
 
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import org.apache.hadoop.hbase.TableName;
@@ -46,6 +50,8 @@ public class RSGroupInfo {
   @Deprecated
   private final SortedSet<TableName> tables;
 
+  private final Map<String, String> configuration;
+
   public RSGroupInfo(String name) {
     this(name, new TreeSet<Address>(), new TreeSet<TableName>());
   }
@@ -54,6 +60,7 @@ public class RSGroupInfo {
     this.name = name;
     this.servers = servers == null ? new TreeSet<>() : new TreeSet<>(servers);
     this.tables = new TreeSet<>();
+    configuration = new HashMap<>();
   }
 
   /**
@@ -65,6 +72,7 @@ public class RSGroupInfo {
     this.name = name;
     this.servers = (servers == null) ? new TreeSet<>() : new TreeSet<>(servers);
     this.tables = (tables == null) ? new TreeSet<>() : new TreeSet<>(tables);
+    configuration = new HashMap<>();
   }
 
   public RSGroupInfo(RSGroupInfo src) {
@@ -115,6 +123,30 @@ public class RSGroupInfo {
   }
 
   /**
+   * Getter for fetching an unmodifiable {@link #configuration} map.
+   */
+  public Map<String, String> getConfiguration() {
+    // shallow pointer copy
+    return Collections.unmodifiableMap(configuration);
+  }
+
+  /**
+   * Setter for storing a configuration setting in {@link #configuration} map.
+   * @param key Config key.
+   * @param value String value.
+   */
+  public void setConfiguration(String key, String value) {
+    configuration.put(key, Objects.requireNonNull(value));
+  }
+
+  /**
+   * Remove a config setting represented by the key from the {@link #configuration} map
+   */
+  public void removeConfiguration(final String key) {
+    configuration.remove(key);
+  }
+
+  /**
    * Get set of tables that are members of the group.
    * @deprecated Since 3.0.0, will be removed in 4.0.0. The rsgroup information will be stored in
    *             the configuration of a table so this will be removed.
diff --git a/hbase-protocol-shaded/src/main/protobuf/server/master/Master.proto b/hbase-protocol-shaded/src/main/protobuf/server/master/Master.proto
index 6c68c80..286c96f 100644
--- a/hbase-protocol-shaded/src/main/protobuf/server/master/Master.proto
+++ b/hbase-protocol-shaded/src/main/protobuf/server/master/Master.proto
@@ -1117,6 +1117,9 @@ service MasterService {
 
   rpc RenameRSGroup(RenameRSGroupRequest)
     returns (RenameRSGroupResponse);
+
+  rpc UpdateRSGroupConfig(UpdateRSGroupConfigRequest)
+  returns (UpdateRSGroupConfigResponse);
 }
 
 // HBCK Service definitions.
diff --git a/hbase-protocol-shaded/src/main/protobuf/server/rsgroup/RSGroup.proto b/hbase-protocol-shaded/src/main/protobuf/server/rsgroup/RSGroup.proto
index bfa54c1..2a46cb0 100644
--- a/hbase-protocol-shaded/src/main/protobuf/server/rsgroup/RSGroup.proto
+++ b/hbase-protocol-shaded/src/main/protobuf/server/rsgroup/RSGroup.proto
@@ -30,4 +30,5 @@ message RSGroupInfo {
   required string name = 1;
   repeated ServerName servers = 4;
   repeated TableName tables = 3;
+  repeated NameStringPair configuration = 5;
 }
diff --git a/hbase-protocol-shaded/src/main/protobuf/server/rsgroup/RSGroupAdmin.proto b/hbase-protocol-shaded/src/main/protobuf/server/rsgroup/RSGroupAdmin.proto
index 860c5aa..9f38d2b 100644
--- a/hbase-protocol-shaded/src/main/protobuf/server/rsgroup/RSGroupAdmin.proto
+++ b/hbase-protocol-shaded/src/main/protobuf/server/rsgroup/RSGroupAdmin.proto
@@ -147,6 +147,14 @@ message RenameRSGroupRequest {
 message RenameRSGroupResponse {
 }
 
+message UpdateRSGroupConfigRequest {
+  required string group_name = 1;
+  repeated NameStringPair configuration = 2;
+}
+
+message UpdateRSGroupConfigResponse {
+}
+
 service RSGroupAdminService {
   rpc GetRSGroupInfo(GetRSGroupInfoRequest)
     returns (GetRSGroupInfoResponse);
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
index f7487ea..ffe4f53 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
@@ -20,6 +20,7 @@ package org.apache.hadoop.hbase.coprocessor;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import org.apache.hadoop.hbase.ClusterMetrics;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
@@ -1359,6 +1360,26 @@ public interface MasterObserver {
     final String oldName, final String newName) throws IOException {}
 
   /**
+   * Called before update rsgroup config.
+   * @param ctx the environment to interact with the framework and master
+   * @param groupName the group name
+   * @param configuration new configuration of the group name to be set
+   */
+  default void preUpdateRSGroupConfig(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                final String groupName, final Map<String, String> configuration)
+      throws IOException {}
+
+  /**
+   * Called after update rsgroup config.
+   * @param ctx the environment to interact with the framework and master
+   * @param groupName the group name
+   * @param configuration new configuration of the group name to be set
+   */
+  default void postUpdateRSGroupConfig(final ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                 final String groupName, final Map<String, String> configuration)
+      throws IOException {}
+
+  /**
    * Called before getting the configured namespaces and tables in the region server group.
    * @param ctx the environment to interact with the framework and master
    * @param groupName name of the region server group
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
index 4d0f25c..28c9c30 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
@@ -21,6 +21,7 @@ package org.apache.hadoop.hbase.master;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.ClusterMetrics;
@@ -1562,6 +1563,26 @@ public class MasterCoprocessorHost
     });
   }
 
+  public void preUpdateRSGroupConfig(final String groupName,
+                                     final Map<String, String> configuration) throws IOException {
+    execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() {
+      @Override
+      protected void call(MasterObserver observer) throws IOException {
+        observer.preUpdateRSGroupConfig(this, groupName, configuration);
+      }
+    });
+  }
+
+  public void postUpdateRSGroupConfig(final String groupName,
+                                      final Map<String, String> configuration) throws IOException {
+    execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() {
+      @Override
+      protected void call(MasterObserver observer) throws IOException {
+        observer.postUpdateRSGroupConfig(this, groupName, configuration);
+      }
+    });
+  }
+
   public void preGetConfiguredNamespacesAndTablesInRSGroup(final String groupName)
     throws IOException {
     execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() {
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
index 2d90301..fc29a38 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
@@ -345,6 +345,8 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.Remo
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.RemoveServersResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.RenameRSGroupRequest;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.RenameRSGroupResponse;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.UpdateRSGroupConfigRequest;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RSGroupAdminProtos.UpdateRSGroupConfigResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.FileArchiveNotificationRequest;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.FileArchiveNotificationResponse;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.RegionServerStatusProtos.GetLastFlushedSequenceIdRequest;
@@ -3248,4 +3250,28 @@ public class MasterRpcServices extends RSRpcServices implements
     }
     return builder.build();
   }
+
+  @Override
+  public UpdateRSGroupConfigResponse updateRSGroupConfig(RpcController controller,
+                                                         UpdateRSGroupConfigRequest request)
+      throws ServiceException {
+    UpdateRSGroupConfigResponse.Builder builder = UpdateRSGroupConfigResponse.newBuilder();
+    String groupName = request.getGroupName();
+    Map<String, String> configuration = new HashMap<>();
+    request.getConfigurationList().forEach(p -> configuration.put(p.getName(), p.getValue()));
+    LOG.info("{} update rsgroup {} configuration {}", master.getClientIdAuditPrefix(), groupName,
+        configuration);
+    try {
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().preUpdateRSGroupConfig(groupName, configuration);
+      }
+      master.getRSGroupInfoManager().updateRSGroupConfig(groupName, configuration);
+      if (master.getMasterCoprocessorHost() != null) {
+        master.getMasterCoprocessorHost().postUpdateRSGroupConfig(groupName, configuration);
+      }
+    } catch (IOException e) {
+      throw new ServiceException(e);
+    }
+    return builder.build();
+  }
 }
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/DisabledRSGroupInfoManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/DisabledRSGroupInfoManager.java
index b86b471..3323acd 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/DisabledRSGroupInfoManager.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/DisabledRSGroupInfoManager.java
@@ -20,6 +20,7 @@ package org.apache.hadoop.hbase.rsgroup;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
@@ -125,4 +126,9 @@ class DisabledRSGroupInfoManager implements RSGroupInfoManager {
   public void renameRSGroup(String oldName, String newName) throws IOException {
   }
 
+  @Override
+  public void updateRSGroupConfig(String groupName, Map<String, String> configuration)
+      throws IOException {
+    throw new DoNotRetryIOException("RSGroup is disabled");
+  }
 }
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java
index 10008f9..350f2b4 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManager.java
@@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.rsgroup;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.master.MasterServices;
@@ -114,4 +115,12 @@ public interface RSGroupInfoManager {
    */
   void renameRSGroup(String oldName, String newName) throws IOException;
 
+  /**
+   * Update RSGroup configuration
+   * @param groupName the group name
+   * @param configuration new configuration of the group name to be set
+   * @throws IOException if a remote or network exception occurs
+   */
+  void updateRSGroupConfig(String groupName, Map<String, String> configuration) throws IOException;
+
 }
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java
index 9e8c635..1cb51cf 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupInfoManagerImpl.java
@@ -1244,4 +1244,12 @@ final class RSGroupInfoManagerImpl implements RSGroupInfoManager {
     setRSGroup(updateTables, newName);
   }
 
+  @Override
+  public synchronized void updateRSGroupConfig(String groupName, Map<String, String> configuration)
+      throws IOException {
+    RSGroupInfo rsGroupInfo = getRSGroupInfo(groupName);
+    rsGroupInfo.getConfiguration().forEach((k, v) -> rsGroupInfo.removeConfiguration(k));
+    configuration.forEach((k, v) -> rsGroupInfo.setConfiguration(k, v));
+    flushConfig();
+  }
 }
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupConfig.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupConfig.java
new file mode 100644
index 0000000..d39ac8f
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupConfig.java
@@ -0,0 +1,99 @@
+/**
+ * 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.hadoop.hbase.rsgroup;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.client.Admin;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.testclassification.RSGroupTests;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TestName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
+
+@Category({ RSGroupTests.class, MediumTests.class })
+public class TestRSGroupConfig extends TestRSGroupsBase {
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+      HBaseClassTestRule.forClass(TestRSGroupConfig.class);
+
+  @Rule
+  public TestName name = new TestName();
+
+  protected static final Logger LOG = LoggerFactory.getLogger(TestRSGroupConfig.class);
+
+  private static Admin admin;
+
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+    TestRSGroupsBase.setUpTestBeforeClass();
+    admin = TEST_UTIL.getAdmin();
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    TestRSGroupsBase.tearDownAfterClass();
+  }
+
+  @Test
+  public void testSetDefaultGroupConfiguration() throws IOException {
+    testSetConfiguration(RSGroupInfo.DEFAULT_GROUP);
+  }
+
+  @Test
+  public void testSetNonDefaultGroupConfiguration() throws IOException {
+    String group = GROUP_PREFIX + name.getMethodName();
+    admin.addRSGroup(group);
+    testSetConfiguration(RSGroupInfo.DEFAULT_GROUP);
+    admin.removeRSGroup(group);
+  }
+
+  private void testSetConfiguration(String group) throws IOException {
+    Map<String, String> configuration = new HashMap<>();
+    configuration.put("aaa", "111");
+    configuration.put("bbb", "222");
+    admin.updateRSGroupConfig(group, configuration);
+    RSGroupInfo rsGroup = admin.getRSGroup(group);
+    Map<String, String> configFromGroup = Maps.newHashMap(rsGroup.getConfiguration());
+    assertNotNull(configFromGroup);
+    assertEquals(2, configFromGroup.size());
+    assertEquals("111", configFromGroup.get("aaa"));
+    // unset configuration
+    admin.updateRSGroupConfig(group, null);
+    rsGroup = admin.getRSGroup(group);
+    configFromGroup = rsGroup.getConfiguration();
+    assertNotNull(configFromGroup);
+    assertEquals(0, configFromGroup.size());
+  }
+
+}
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdmin.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdmin.java
index 7e1ec6e..58ab1ca 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdmin.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/VerifyingRSGroupAdmin.java
@@ -822,6 +822,13 @@ public class VerifyingRSGroupAdmin implements Admin, Closeable {
     verify();
   }
 
+  @Override
+  public void updateRSGroupConfig(String groupName, Map<String, String> configuration)
+      throws IOException {
+    admin.updateRSGroupConfig(groupName, configuration);
+    verify();
+  }
+
   private void verify() throws IOException {
     Map<String, RSGroupInfo> groupMap = Maps.newHashMap();
     Set<RSGroupInfo> zList = Sets.newHashSet();
diff --git a/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb b/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb
index fd39634..2027c77 100644
--- a/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb
+++ b/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb
@@ -191,5 +191,42 @@ module Hbase
     def rename_rsgroup(oldname, newname)
       @admin.renameRSGroup(oldname, newname)
     end
+
+    #----------------------------------------------------------------------------------------------
+    # modify a rsgroup configuration
+    def alter_rsgroup_config(rsgroup_name, *args)
+      # Fail if table name is not a string
+      raise(ArgumentError, 'RSGroup name must be of type String') unless rsgroup_name.is_a?(String)
+
+      group = @admin.getRSGroup(rsgroup_name)
+
+      raise(ArgumentError, 'RSGroup does not exist') unless group
+
+      configuration = java.util.HashMap.new
+      configuration.putAll(group.getConfiguration)
+
+      # Flatten params array
+      args = args.flatten.compact
+
+      # Start defining the table
+      args.each do |arg|
+        unless arg.is_a?(Hash)
+          raise(ArgumentError, "#{arg.class} of #{arg.inspect} is not of Hash type")
+        end
+        method = arg[METHOD]
+        if method == 'unset'
+          configuration.remove(arg[NAME])
+        elsif method == 'set'
+          arg.delete(METHOD)
+          for k, v in arg
+            v = v.to_s unless v.nil?
+            configuration.put(k, v)
+          end
+        else
+          raise(ArgumentError, "Unknown method #{method}")
+        end
+      end
+      @admin.updateRSGroupConfig(rsgroup_name, configuration)
+    end
   end
 end
diff --git a/hbase-shell/src/main/ruby/shell.rb b/hbase-shell/src/main/ruby/shell.rb
index 885394c..92e52fa 100644
--- a/hbase-shell/src/main/ruby/shell.rb
+++ b/hbase-shell/src/main/ruby/shell.rb
@@ -509,5 +509,7 @@ Shell.load_command_group(
     get_table_rsgroup
     remove_servers_rsgroup
     rename_rsgroup
+    alter_rsgroup_config
+    show_rsgroup_config
   ]
 )
diff --git a/hbase-shell/src/main/ruby/shell/commands/alter_rsgroup_config.rb b/hbase-shell/src/main/ruby/shell/commands/alter_rsgroup_config.rb
new file mode 100644
index 0000000..d27edd9
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/alter_rsgroup_config.rb
@@ -0,0 +1,38 @@
+#
+#
+# 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 AlterRsgroupConfig < Command
+      def help
+        <<-EOF
+Alter RSGroup configuration.
+Example:
+    hbase> alter_rsgroup_config 'grp1', {METHOD => 'set', 'PROPERTY_NAME' => 'PROPERTY_VALUE'}
+To delete a property:
+    hbase> alter_rsgroup_config 'grp1', {METHOD => 'unset', NAME=>'PROPERTY_NAME'}
+EOF
+      end
+
+      def command(group, args)
+        rsgroup_admin.alter_rsgroup_config(group, args)
+      end
+    end
+  end
+end
diff --git a/hbase-shell/src/main/ruby/shell/commands/show_rsgroup_config.rb b/hbase-shell/src/main/ruby/shell/commands/show_rsgroup_config.rb
new file mode 100644
index 0000000..88ebd5c
--- /dev/null
+++ b/hbase-shell/src/main/ruby/shell/commands/show_rsgroup_config.rb
@@ -0,0 +1,43 @@
+#
+#
+# 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 ShowRsgroupConfig < Command
+      def help
+        <<-EOF
+Show the configuration of a special RSGroup.
+Example:
+
+    hbase> show_rsgroup_config 'group'
+
+EOF
+      end
+
+      def command(group)
+        formatter.header(%w['KEY' 'VALUE'])
+        config = rsgroup_admin.get_rsgroup(group).getConfiguration
+        config.each { |key, val|
+          formatter.row([key, val])
+        }
+        formatter.footer(config.size)
+      end
+    end
+  end
+end
diff --git a/hbase-shell/src/test/ruby/shell/rsgroup_shell_test.rb b/hbase-shell/src/test/ruby/shell/rsgroup_shell_test.rb
index d7bccdb..33a6c49 100644
--- a/hbase-shell/src/test/ruby/shell/rsgroup_shell_test.rb
+++ b/hbase-shell/src/test/ruby/shell/rsgroup_shell_test.rb
@@ -140,5 +140,19 @@ module Hbase
       assert_equal(1, @admin.listTablesInRSGroup(new_rs_group_name).count)
       assert_true(@admin.listTablesInRSGroup(new_rs_group_name).contains(org.apache.hadoop.hbase.TableName.valueOf(table_name)))
     end
+
+    define_test 'Test alter rsgroup configuration' do
+      group_name = 'grp1'
+      @shell.command('add_rsgroup', group_name)
+      assert_not_nil(@admin.getRSGroup(group_name))
+
+      @hbase.rsgroup_admin.alter_rsgroup_config(group_name, {'METHOD' => 'set', 'a' => 'a'})
+      assert_equal(1, @admin.getRSGroup(group_name).getConfiguration.size)
+      @hbase.rsgroup_admin.alter_rsgroup_config(group_name, {'METHOD' => 'unset', 'NAME' => 'a'})
+      assert_equal(0, @admin.getRSGroup(group_name).getConfiguration.size)
+
+      @shell.command('remove_rsgroup', group_name)
+      assert_nil(@admin.getRSGroup(group_name))
+    end
   end
 end
diff --git a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java
index 2e24897..81c700d 100644
--- a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java
+++ b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftAdmin.java
@@ -1261,4 +1261,10 @@ public class ThriftAdmin implements Admin {
   public void renameRSGroup(String oldName, String newName) throws IOException {
     throw new NotImplementedException("renameRSGroup not supported in ThriftAdmin");
   }
+
+  @Override
+  public void updateRSGroupConfig(String groupName, Map<String, String> configuration)
+      throws IOException {
+    throw new NotImplementedException("updateRSGroupConfig not supported in ThriftAdmin");
+  }
 }