You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by br...@apache.org on 2021/04/22 05:08:13 UTC

[hbase] branch branch-2 updated: HBASE-25766 Introduce RegionSplitRestriction that restricts the pattern of the split point

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

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


The following commit(s) were added to refs/heads/branch-2 by this push:
     new f4f8430  HBASE-25766 Introduce RegionSplitRestriction that restricts the pattern of the split point
f4f8430 is described below

commit f4f84302fa6b53acb9a0b7b13c3df55b39a44345
Author: Toshihiro Suzuki <br...@gmail.com>
AuthorDate: Thu Apr 22 13:53:36 2021 +0900

    HBASE-25766 Introduce RegionSplitRestriction that restricts the pattern of the split point
    
    Signed-off-by: Duo Zhang <zh...@apache.org>
    Signed-off-by: Michael Stack <st...@apache.org>
---
 .../assignment/SplitTableRegionProcedure.java      |  25 ++-
 .../DelimitedKeyPrefixRegionSplitPolicy.java       |   4 +
 ... DelimitedKeyPrefixRegionSplitRestriction.java} |  69 ++++----
 .../apache/hadoop/hbase/regionserver/HRegion.java  |   7 +
 .../regionserver/KeyPrefixRegionSplitPolicy.java   |   4 +
 .../KeyPrefixRegionSplitRestriction.java           |  76 +++++++++
 .../regionserver/NoRegionSplitRestriction.java     |  40 +++++
 .../hbase/regionserver/RegionSplitRestriction.java | 129 +++++++++++++++
 .../regionserver/TestRegionSplitRestriction.java   | 184 +++++++++++++++++++++
 9 files changed, 496 insertions(+), 42 deletions(-)

diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java
index 3ed6058..09ac827 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java
@@ -62,6 +62,7 @@ import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
 import org.apache.hadoop.hbase.regionserver.HStore;
 import org.apache.hadoop.hbase.regionserver.HStoreFile;
 import org.apache.hadoop.hbase.regionserver.RegionSplitPolicy;
+import org.apache.hadoop.hbase.regionserver.RegionSplitRestriction;
 import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.CommonFSUtils;
@@ -110,6 +111,21 @@ public class SplitTableRegionProcedure
     // we fail-fast on construction. There it skips the split with just a warning.
     checkOnline(env, regionToSplit);
     this.bestSplitRow = splitRow;
+    TableDescriptor tableDescriptor = env.getMasterServices().getTableDescriptors()
+      .get(getTableName());
+    Configuration conf = env.getMasterConfiguration();
+    if (hasBestSplitRow()) {
+      // Apply the split restriction for the table to the user-specified split point
+      RegionSplitRestriction splitRestriction =
+        RegionSplitRestriction.create(tableDescriptor, conf);
+      byte[] restrictedSplitRow = splitRestriction.getRestrictedSplitPoint(bestSplitRow);
+      if (!Bytes.equals(bestSplitRow, restrictedSplitRow)) {
+        LOG.warn("The specified split point {} violates the split restriction of the table. "
+            + "Using {} as a split point.", Bytes.toStringBinary(bestSplitRow),
+          Bytes.toStringBinary(restrictedSplitRow));
+        bestSplitRow = restrictedSplitRow;
+      }
+    }
     checkSplittable(env, regionToSplit);
     final TableName table = regionToSplit.getTable();
     final long rid = getDaughterRegionIdTimestamp(regionToSplit);
@@ -125,15 +141,14 @@ public class SplitTableRegionProcedure
         .setSplit(false)
         .setRegionId(rid)
         .build();
-    TableDescriptor htd = env.getMasterServices().getTableDescriptors().get(getTableName());
-    if(htd.getRegionSplitPolicyClassName() != null) {
+    if(tableDescriptor.getRegionSplitPolicyClassName() != null) {
       // Since we don't have region reference here, creating the split policy instance without it.
       // This can be used to invoke methods which don't require Region reference. This instantiation
       // of a class on Master-side though it only makes sense on the RegionServer-side is
       // for Phoenix Local Indexing. Refer HBASE-12583 for more information.
       Class<? extends RegionSplitPolicy> clazz =
-          RegionSplitPolicy.getSplitPolicyClass(htd, env.getMasterConfiguration());
-      this.splitPolicy = ReflectionUtils.newInstance(clazz, env.getMasterConfiguration());
+        RegionSplitPolicy.getSplitPolicyClass(tableDescriptor, conf);
+      this.splitPolicy = ReflectionUtils.newInstance(clazz, conf);
     }
   }
 
@@ -219,7 +234,7 @@ public class SplitTableRegionProcedure
       throw e;
     }
 
-    if (bestSplitRow == null || bestSplitRow.length == 0) {
+    if (!hasBestSplitRow()) {
       throw new DoNotRetryIOException("Region not splittable because bestSplitPoint = null, " +
         "maybe table is too small for auto split. For force split, try specifying split row");
     }
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DelimitedKeyPrefixRegionSplitPolicy.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DelimitedKeyPrefixRegionSplitPolicy.java
index 33caa93..91781b0 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DelimitedKeyPrefixRegionSplitPolicy.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DelimitedKeyPrefixRegionSplitPolicy.java
@@ -37,7 +37,11 @@ import org.apache.hadoop.hbase.util.Bytes;
  * <code>userid_eventtype_eventid</code>, and use prefix delimiter _, this split policy
  * ensures that all rows starting with the same userid, belongs to the same region.
  * @see KeyPrefixRegionSplitPolicy
+ *
+ * @deprecated since 2.5.0 and will be removed in 4.0.0. Use {@link RegionSplitRestriction},
+ *   instead.
  */
+@Deprecated
 @InterfaceAudience.Private
 public class DelimitedKeyPrefixRegionSplitPolicy extends IncreasingToUpperBoundRegionSplitPolicy {
 
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DelimitedKeyPrefixRegionSplitPolicy.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DelimitedKeyPrefixRegionSplitRestriction.java
similarity index 50%
copy from hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DelimitedKeyPrefixRegionSplitPolicy.java
copy to hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DelimitedKeyPrefixRegionSplitRestriction.java
index 33caa93..fa68648 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DelimitedKeyPrefixRegionSplitPolicy.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DelimitedKeyPrefixRegionSplitRestriction.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -15,68 +15,63 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.hadoop.hbase.regionserver;
 
+import java.io.IOException;
 import java.util.Arrays;
-
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.client.TableDescriptor;
+import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.yetus.audience.InterfaceAudience;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.hadoop.hbase.util.Bytes;
 
 /**
- * A custom RegionSplitPolicy implementing a SplitPolicy that groups
- * rows by a prefix of the row-key with a delimiter. Only the first delimiter
- * for the row key will define the prefix of the row key that is used for grouping.
- *
+ * A {@link RegionSplitRestriction} implementation that groups rows by a prefix of the row-key with
+ * a delimiter. Only the first delimiter for the row key will define the prefix of the row key that
+ * is used for grouping.
+ * <p>
  * This ensures that a region is not split "inside" a prefix of a row key.
  * I.e. rows can be co-located in a region by their prefix.
  *
  * As an example, if you have row keys delimited with <code>_</code>, like
- * <code>userid_eventtype_eventid</code>, and use prefix delimiter _, this split policy
- * ensures that all rows starting with the same userid, belongs to the same region.
- * @see KeyPrefixRegionSplitPolicy
+ * <code>userid_eventtype_eventid</code>, and use prefix delimiter _, this split policy ensures
+ * that all rows starting with the same userid, belongs to the same region.
  */
 @InterfaceAudience.Private
-public class DelimitedKeyPrefixRegionSplitPolicy extends IncreasingToUpperBoundRegionSplitPolicy {
+public class DelimitedKeyPrefixRegionSplitRestriction extends RegionSplitRestriction {
+  private static final Logger LOG =
+    LoggerFactory.getLogger(DelimitedKeyPrefixRegionSplitRestriction.class);
 
-  private static final Logger LOG = LoggerFactory
-      .getLogger(DelimitedKeyPrefixRegionSplitPolicy.class);
-  public static final String DELIMITER_KEY = "DelimitedKeyPrefixRegionSplitPolicy.delimiter";
+  public static final String DELIMITER_KEY =
+    "hbase.regionserver.region.split_restriction.delimiter";
 
   private byte[] delimiter = null;
 
   @Override
-  public String toString() {
-    return "DelimitedKeyPrefixRegionSplitPolicy{" + "delimiter=" + Bytes.toStringBinary(delimiter) +
-      ", " + super.toString() + '}';
-  }
-
-  @Override
-  protected void configureForRegion(HRegion region) {
-    super.configureForRegion(region);
-    // read the prefix length from the table descriptor
-    String delimiterString = region.getTableDescriptor().getValue(DELIMITER_KEY);
+  public void initialize(TableDescriptor tableDescriptor, Configuration conf) throws IOException {
+    String delimiterString = tableDescriptor.getValue(DELIMITER_KEY);
     if (delimiterString == null || delimiterString.length() == 0) {
-      LOG.error(DELIMITER_KEY + " not specified for table " + region.getTableDescriptor().getTableName() +
-        ". Using default RegionSplitPolicy");
-      return;
+      delimiterString = conf.get(DELIMITER_KEY);
+      if (delimiterString == null || delimiterString.length() == 0) {
+        LOG.error("{} not specified for table {}. "
+          + "Using the default RegionSplitRestriction", DELIMITER_KEY,
+          tableDescriptor.getTableName());
+        return;
+      }
     }
     delimiter = Bytes.toBytes(delimiterString);
   }
 
   @Override
-  protected byte[] getSplitPoint() {
-    byte[] splitPoint = super.getSplitPoint();
-    if (splitPoint != null && delimiter != null) {
-
-      //find the first occurrence of delimiter in split point
-      int index =
-        org.apache.hbase.thirdparty.com.google.common.primitives.Bytes.indexOf(splitPoint, delimiter);
+  public byte[] getRestrictedSplitPoint(byte[] splitPoint) {
+    if (delimiter != null) {
+      // find the first occurrence of delimiter in split point
+      int index = org.apache.hbase.thirdparty.com.google.common.primitives.Bytes.indexOf(
+        splitPoint, delimiter);
       if (index < 0) {
-        LOG.warn("Delimiter " + Bytes.toString(delimiter) + "  not found for split key "
-            + Bytes.toString(splitPoint));
+        LOG.warn("Delimiter {} not found for split key {}", Bytes.toString(delimiter),
+          Bytes.toStringBinary(splitPoint));
         return splitPoint;
       }
 
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
index 9220b2f..d7cfed9 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
@@ -697,6 +697,7 @@ public class HRegion implements HeapSize, PropagatingConfigurationObserver, Regi
 
   private TableDescriptor htableDescriptor = null;
   private RegionSplitPolicy splitPolicy;
+  private RegionSplitRestriction splitRestriction;
   private FlushPolicy flushPolicy;
 
   private final MetricsRegion metricsRegion;
@@ -1042,6 +1043,9 @@ public class HRegion implements HeapSize, PropagatingConfigurationObserver, Regi
     // Initialize split policy
     this.splitPolicy = RegionSplitPolicy.create(this, conf);
 
+    // Initialize split restriction
+    splitRestriction = RegionSplitRestriction.create(getTableDescriptor(), conf);
+
     // Initialize flush policy
     this.flushPolicy = FlushPolicyFactory.create(this, conf);
 
@@ -8090,6 +8094,9 @@ public class HRegion implements HeapSize, PropagatingConfigurationObserver, Regi
     }
 
     byte[] ret = splitPolicy.getSplitPoint();
+    if (ret != null && ret.length > 0) {
+      ret = splitRestriction.getRestrictedSplitPoint(ret);
+    }
 
     if (ret != null) {
       try {
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/KeyPrefixRegionSplitPolicy.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/KeyPrefixRegionSplitPolicy.java
index 29c7d11..5cf6731 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/KeyPrefixRegionSplitPolicy.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/KeyPrefixRegionSplitPolicy.java
@@ -29,7 +29,11 @@ import org.slf4j.LoggerFactory;
  *
  * This ensures that a region is not split "inside" a prefix of a row key.
  * I.e. rows can be co-located in a region by their prefix.
+ *
+ * @deprecated since 2.5.0 and will be removed in 4.0.0. Use {@link RegionSplitRestriction},
+ *   instead.
  */
+@Deprecated
 @InterfaceAudience.Private
 public class KeyPrefixRegionSplitPolicy extends IncreasingToUpperBoundRegionSplitPolicy {
   private static final Logger LOG = LoggerFactory
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/KeyPrefixRegionSplitRestriction.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/KeyPrefixRegionSplitRestriction.java
new file mode 100644
index 0000000..41fcc2a
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/KeyPrefixRegionSplitRestriction.java
@@ -0,0 +1,76 @@
+/*
+ * 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.regionserver;
+
+import java.io.IOException;
+import java.util.Arrays;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.client.TableDescriptor;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link RegionSplitRestriction} implementation that groups rows by a prefix of the row-key.
+ * <p>
+ * This ensures that a region is not split "inside" a prefix of a row key.
+ * I.e. rows can be co-located in a region by their prefix.
+ */
+@InterfaceAudience.Private
+public class KeyPrefixRegionSplitRestriction extends RegionSplitRestriction {
+  private static final Logger LOG =
+    LoggerFactory.getLogger(KeyPrefixRegionSplitRestriction.class);
+
+  public static final String PREFIX_LENGTH_KEY =
+    "hbase.regionserver.region.split_restriction.prefix_length";
+
+  private int prefixLength;
+
+  @Override
+  public void initialize(TableDescriptor tableDescriptor, Configuration conf) throws IOException {
+    String prefixLengthString = tableDescriptor.getValue(PREFIX_LENGTH_KEY);
+    if (prefixLengthString == null) {
+      prefixLengthString = conf.get(PREFIX_LENGTH_KEY);
+      if (prefixLengthString == null) {
+        LOG.error("{} not specified for table {}. "
+          + "Using the default RegionSplitRestriction", PREFIX_LENGTH_KEY,
+          tableDescriptor.getTableName());
+        return;
+      }
+    }
+    try {
+      prefixLength = Integer.parseInt(prefixLengthString);
+    } catch (NumberFormatException ignored) {
+    }
+    if (prefixLength <= 0) {
+      LOG.error("Invalid value for {} for table {}:{}. "
+        + "Using the default RegionSplitRestriction", PREFIX_LENGTH_KEY,
+        tableDescriptor.getTableName(), prefixLengthString);
+    }
+  }
+
+  @Override
+  public byte[] getRestrictedSplitPoint(byte[] splitPoint) {
+    if (prefixLength > 0) {
+      // group split keys by a prefix
+      return Arrays.copyOf(splitPoint, Math.min(prefixLength, splitPoint.length));
+    } else {
+      return splitPoint;
+    }
+  }
+}
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/NoRegionSplitRestriction.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/NoRegionSplitRestriction.java
new file mode 100644
index 0000000..662c164
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/NoRegionSplitRestriction.java
@@ -0,0 +1,40 @@
+/*
+ * 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.regionserver;
+
+import java.io.IOException;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.client.TableDescriptor;
+import org.apache.yetus.audience.InterfaceAudience;
+
+/**
+ * A {@link RegionSplitRestriction} implementation that does nothing.
+ */
+@InterfaceAudience.Private
+public class NoRegionSplitRestriction extends RegionSplitRestriction {
+
+  @Override
+  public void initialize(TableDescriptor tableDescriptor, Configuration conf) throws IOException {
+  }
+
+  @Override
+  public byte[] getRestrictedSplitPoint(byte[] splitPoint) {
+    // Do nothing
+    return splitPoint;
+  }
+}
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionSplitRestriction.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionSplitRestriction.java
new file mode 100644
index 0000000..3a1925c
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionSplitRestriction.java
@@ -0,0 +1,129 @@
+/*
+ * 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.regionserver;
+
+import java.io.IOException;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.client.TableDescriptor;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A split restriction that restricts the pattern of the split point.
+ * <p>
+ * The difference between {@link RegionSplitPolicy} and RegionSplitRestriction is that
+ * RegionSplitRestriction defines how to split while {@link RegionSplitPolicy} defines when we need
+ * to split.
+ * <p>
+ * We can specify a split restriction, "KeyPrefix" or "DelimitedKeyPrefix", to a table with the
+ * "hbase.regionserver.region.split_restriction.type" property. The "KeyPrefix" split restriction
+ * groups rows by a prefix of the row-key. And the "DelimitedKeyPrefix" split restriction groups
+ * rows by a prefix of the row-key with a delimiter.
+ *
+ * For example:
+ * <pre>
+ * <code>
+ * # Create a table with a "KeyPrefix" split restriction, where the prefix length is 2 bytes
+ * hbase> create 'tbl1', 'fam',
+ *   {CONFIGURATION => {'hbase.regionserver.region.split_restriction.type' => 'KeyPrefix',
+ *                      'hbase.regionserver.region.split_restriction.prefix_length' => '2'}}
+ *
+ * # Create a table with a "DelimitedKeyPrefix" split restriction, where the delimiter is a comma
+ * hbase> create 'tbl2', 'fam',
+ *   {CONFIGURATION => {'hbase.regionserver.region.split_restriction.type' => 'DelimitedKeyPrefix',
+ *                      'hbase.regionserver.region.split_restriction.delimiter' => ','}}
+ * </code>
+ * </pre>
+ *
+ * Instead of specifying a split restriction to a table directly, we can also set the properties
+ * in hbase-site.xml. In this case, the specified split restriction is applied for all the tables.
+ * <p>
+ * Note that the split restriction is also applied to a user-specified split point so that we don't
+ * allow users to break the restriction.
+ *
+ * @see NoRegionSplitRestriction
+ * @see KeyPrefixRegionSplitRestriction
+ * @see DelimitedKeyPrefixRegionSplitRestriction
+ */
+@InterfaceAudience.Private
+public abstract class RegionSplitRestriction {
+  private static final Logger LOG = LoggerFactory.getLogger(RegionSplitRestriction.class);
+
+  public static final String RESTRICTION_TYPE_KEY =
+    "hbase.regionserver.region.split_restriction.type";
+
+  public static final String RESTRICTION_TYPE_NONE = "None";
+  public static final String RESTRICTION_TYPE_KEY_PREFIX = "KeyPrefix";
+  public static final String RESTRICTION_TYPE_DELIMITED_KEY_PREFIX = "DelimitedKeyPrefix";
+
+  /**
+   * Create the RegionSplitRestriction configured for the given table.
+   *
+   * @param tableDescriptor the table descriptor
+   * @param conf the configuration
+   * @return a RegionSplitRestriction instance
+   * @throws IOException if an error occurs
+   */
+  public static RegionSplitRestriction create(TableDescriptor tableDescriptor,
+    Configuration conf) throws IOException {
+    String type = tableDescriptor.getValue(RESTRICTION_TYPE_KEY);
+    if (type == null) {
+      type = conf.get(RESTRICTION_TYPE_KEY, RESTRICTION_TYPE_NONE);
+    }
+
+    RegionSplitRestriction ret;
+    switch (type) {
+      case RESTRICTION_TYPE_NONE:
+        ret = new NoRegionSplitRestriction();
+        break;
+      case RESTRICTION_TYPE_KEY_PREFIX:
+        ret = new KeyPrefixRegionSplitRestriction();
+        break;
+      case RESTRICTION_TYPE_DELIMITED_KEY_PREFIX:
+        ret = new DelimitedKeyPrefixRegionSplitRestriction();
+        break;
+      default:
+        LOG.warn("Invalid RegionSplitRestriction type specified: {}. "
+          + "Using the default RegionSplitRestriction", type);
+        ret = new NoRegionSplitRestriction();
+        break;
+    }
+    ret.initialize(tableDescriptor, conf);
+    return ret;
+  }
+
+  /**
+   * Initialize the RegionSplitRestriction instance
+   *
+   * @param tableDescriptor the table descriptor
+   * @param conf the configuration
+   * @throws IOException if an error occurs
+   */
+  public abstract void initialize(TableDescriptor tableDescriptor, Configuration conf)
+    throws IOException;
+
+  /**
+   * Returns a restricted split point.
+   *
+   * @param splitPoint the split point determined by {@link RegionSplitPolicy} or specified by a
+   *   user manually
+   * @return the restricted split point
+   */
+  public abstract byte[] getRestrictedSplitPoint(byte[] splitPoint);
+}
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionSplitRestriction.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionSplitRestriction.java
new file mode 100644
index 0000000..329a7af
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionSplitRestriction.java
@@ -0,0 +1,184 @@
+/*
+ * 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.regionserver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.client.TableDescriptor;
+import org.apache.hadoop.hbase.testclassification.RegionServerTests;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Category({ RegionServerTests.class, SmallTests.class })
+public class TestRegionSplitRestriction {
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+    HBaseClassTestRule.forClass(TestRegionSplitRestriction.class);
+
+  Configuration conf;
+  @Mock TableDescriptor tableDescriptor;
+
+  @Before
+  public void setup() {
+    MockitoAnnotations.initMocks(this);
+
+    conf = new Configuration();
+  }
+
+  @Test
+  public void testWhenTableDescriptorReturnsNoneType() throws IOException {
+    when(tableDescriptor.getValue(RegionSplitRestriction.RESTRICTION_TYPE_KEY))
+      .thenReturn(RegionSplitRestriction.RESTRICTION_TYPE_NONE);
+
+    RegionSplitRestriction splitRestriction =
+      RegionSplitRestriction.create(tableDescriptor, conf);
+    assertTrue(splitRestriction instanceof NoRegionSplitRestriction);
+  }
+
+  @Test
+  public void testWhenTableDescriptorReturnsKeyPrefixType() throws IOException {
+    when(tableDescriptor.getValue(RegionSplitRestriction.RESTRICTION_TYPE_KEY))
+      .thenReturn(RegionSplitRestriction.RESTRICTION_TYPE_KEY_PREFIX);
+
+    RegionSplitRestriction splitRestriction =
+      RegionSplitRestriction.create(tableDescriptor, conf);
+    assertTrue(splitRestriction instanceof KeyPrefixRegionSplitRestriction);
+  }
+
+  @Test
+  public void testWhenTableDescriptorReturnsDelimitedKeyPrefixType() throws IOException {
+    when(tableDescriptor.getValue(RegionSplitRestriction.RESTRICTION_TYPE_KEY))
+      .thenReturn(RegionSplitRestriction.RESTRICTION_TYPE_DELIMITED_KEY_PREFIX);
+
+    RegionSplitRestriction splitRestriction =
+      RegionSplitRestriction.create(tableDescriptor, conf);
+    assertTrue(splitRestriction instanceof DelimitedKeyPrefixRegionSplitRestriction);
+  }
+
+  @Test
+  public void testWhenConfigurationReturnsNoneType() throws IOException {
+    conf.set(RegionSplitRestriction.RESTRICTION_TYPE_KEY,
+      RegionSplitRestriction.RESTRICTION_TYPE_NONE);
+
+    RegionSplitRestriction splitRestriction =
+      RegionSplitRestriction.create(tableDescriptor, conf);
+    assertTrue(splitRestriction instanceof NoRegionSplitRestriction);
+  }
+
+  @Test
+  public void testWhenConfigurationReturnsKeyPrefixType() throws IOException {
+    conf.set(RegionSplitRestriction.RESTRICTION_TYPE_KEY,
+      RegionSplitRestriction.RESTRICTION_TYPE_KEY_PREFIX);
+
+    RegionSplitRestriction splitRestriction =
+      RegionSplitRestriction.create(tableDescriptor, conf);
+    assertTrue(splitRestriction instanceof KeyPrefixRegionSplitRestriction);
+  }
+
+  @Test
+  public void testWhenConfigurationReturnsDelimitedKeyPrefixType() throws IOException {
+    conf.set(RegionSplitRestriction.RESTRICTION_TYPE_KEY,
+      RegionSplitRestriction.RESTRICTION_TYPE_DELIMITED_KEY_PREFIX);
+
+    RegionSplitRestriction splitRestriction =
+      RegionSplitRestriction.create(tableDescriptor, conf);
+    assertTrue(splitRestriction instanceof DelimitedKeyPrefixRegionSplitRestriction);
+  }
+
+  @Test
+  public void testWhenTableDescriptorAndConfigurationReturnNull() throws IOException {
+    RegionSplitRestriction splitRestriction =
+      RegionSplitRestriction.create(tableDescriptor, conf);
+    assertTrue(splitRestriction instanceof NoRegionSplitRestriction);
+  }
+
+  @Test
+  public void testWhenTableDescriptorReturnsInvalidType() throws IOException {
+    when(tableDescriptor.getValue(RegionSplitRestriction.RESTRICTION_TYPE_KEY))
+      .thenReturn("Invalid");
+
+    RegionSplitRestriction splitRestriction =
+      RegionSplitRestriction.create(tableDescriptor, conf);
+    assertTrue(splitRestriction instanceof NoRegionSplitRestriction);
+  }
+
+  @Test
+  public void testNoneRegionSplitRestriction() throws IOException {
+    when(tableDescriptor.getValue(RegionSplitRestriction.RESTRICTION_TYPE_KEY))
+      .thenReturn(RegionSplitRestriction.RESTRICTION_TYPE_NONE);
+
+    NoRegionSplitRestriction noRegionSplitRestriction =
+      (NoRegionSplitRestriction) RegionSplitRestriction.create(tableDescriptor, conf);
+
+    byte[] restrictedSplit =
+      noRegionSplitRestriction.getRestrictedSplitPoint(Bytes.toBytes("abcd"));
+    assertEquals("abcd", Bytes.toString(restrictedSplit));
+  }
+
+  @Test
+  public void testKeyPrefixRegionSplitRestriction() throws IOException {
+    when(tableDescriptor.getValue(RegionSplitRestriction.RESTRICTION_TYPE_KEY))
+      .thenReturn(RegionSplitRestriction.RESTRICTION_TYPE_KEY_PREFIX);
+    when(tableDescriptor.getValue(KeyPrefixRegionSplitRestriction.PREFIX_LENGTH_KEY))
+      .thenReturn("2");
+
+    KeyPrefixRegionSplitRestriction keyPrefixRegionSplitRestriction =
+      (KeyPrefixRegionSplitRestriction) RegionSplitRestriction.create(
+        tableDescriptor, conf);
+
+    byte[] restrictedSplit =
+      keyPrefixRegionSplitRestriction.getRestrictedSplitPoint(Bytes.toBytes("abcd"));
+    assertEquals("ab", Bytes.toString(restrictedSplit));
+
+    restrictedSplit =
+      keyPrefixRegionSplitRestriction.getRestrictedSplitPoint(Bytes.toBytes("a"));
+    assertEquals("a", Bytes.toString(restrictedSplit));
+  }
+
+  @Test
+  public void testDelimitedKeyPrefixRegionSplitRestriction() throws IOException {
+    when(tableDescriptor.getValue(RegionSplitRestriction.RESTRICTION_TYPE_KEY))
+      .thenReturn(RegionSplitRestriction.RESTRICTION_TYPE_DELIMITED_KEY_PREFIX);
+    when(tableDescriptor.getValue(DelimitedKeyPrefixRegionSplitRestriction.DELIMITER_KEY))
+      .thenReturn(",");
+
+    DelimitedKeyPrefixRegionSplitRestriction delimitedKeyPrefixRegionSplitRestriction =
+      (DelimitedKeyPrefixRegionSplitRestriction) RegionSplitRestriction.create(
+        tableDescriptor, conf);
+
+    byte[] restrictedSplit = delimitedKeyPrefixRegionSplitRestriction
+      .getRestrictedSplitPoint(Bytes.toBytes("ab,cd"));
+    assertEquals("ab", Bytes.toString(restrictedSplit));
+
+    restrictedSplit = delimitedKeyPrefixRegionSplitRestriction
+      .getRestrictedSplitPoint(Bytes.toBytes("ijk"));
+    assertEquals("ijk", Bytes.toString(restrictedSplit));
+  }
+}