Index: src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionSplitPolicy.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionSplitPolicy.java (revision 1238830) +++ src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionSplitPolicy.java (working copy) @@ -20,6 +20,7 @@ import static org.junit.Assert.*; import java.io.IOException; +import java.util.Arrays; import java.util.TreeMap; import org.apache.hadoop.conf.Configuration; @@ -30,6 +31,24 @@ import org.junit.experimental.categories.Category; import org.mockito.Mockito; +/** + * Custom split policy to overriding getSplitPoint. + * This one ensures that a region is not split "inside" + * a prefix a row key. I.e. rows can be co-located by + * their prefix. + */ +class PrefixSplitKeyPolicy extends ConstantSizeRegionSplitPolicy { + @Override + protected byte[] getSplitPoint() { + byte[] splitPoint = super.getSplitPoint(); + if (splitPoint != null && splitPoint.length > 0) { + return Arrays.copyOf(splitPoint, Math.min(2, splitPoint.length)); + } else { + return splitPoint; + } + } +} + @Category(SmallTests.class) public class TestRegionSplitPolicy { @@ -71,7 +90,41 @@ assertEquals(9999L, policy.getDesiredMaxFileSize()); } + /** + * Test setting up a customized split policy + */ @Test + public void testCustomPolicy() throws IOException { + HTableDescriptor myHtd = new HTableDescriptor(); + myHtd.setValue(HTableDescriptor.SPLIT_POLICY, + PrefixSplitKeyPolicy.class.getName()); + + HRegion myMockRegion = Mockito.mock(HRegion.class); + Mockito.doReturn(myHtd).when(myMockRegion).getTableDesc(); + Mockito.doReturn(stores).when(myMockRegion).getStores(); + + Store mockStore = Mockito.mock(Store.class); + Mockito.doReturn(2000L).when(mockStore).getSize(); + Mockito.doReturn(true).when(mockStore).canSplit(); + Mockito.doReturn(Bytes.toBytes("abcd")).when(mockStore).getSplitPoint(); + stores.put(new byte[] { 1 }, mockStore); + + PrefixSplitKeyPolicy policy = (PrefixSplitKeyPolicy) RegionSplitPolicy + .create(myMockRegion, conf); + + assertEquals("ab", Bytes.toString(policy.getSplitPoint())); + + Mockito.doReturn(true).when(myMockRegion).shouldForceSplit(); + Mockito.doReturn(Bytes.toBytes("efgh")).when(myMockRegion) + .getExplicitSplitPoint(); + + policy = (PrefixSplitKeyPolicy) RegionSplitPolicy + .create(myMockRegion, conf); + + assertEquals("ef", Bytes.toString(policy.getSplitPoint())); + } + + @Test public void testConstantSizePolicy() throws IOException { htd.setMaxFileSize(1024L); Index: src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java (revision 1238830) +++ src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java (working copy) @@ -69,7 +69,7 @@ private static final String FAMILIES = "FAMILIES"; - private static final String SPLIT_POLICY = "SPLIT_POLICY"; + public static final String SPLIT_POLICY = "SPLIT_POLICY"; /** * INTERNAL Used by HBase Shell interface to access this metadata Index: src/main/java/org/apache/hadoop/hbase/regionserver/ConstantSizeRegionSplitPolicy.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/ConstantSizeRegionSplitPolicy.java (revision 1238830) +++ src/main/java/org/apache/hadoop/hbase/regionserver/ConstantSizeRegionSplitPolicy.java (working copy) @@ -23,12 +23,13 @@ * A {@link RegionSplitPolicy} implementation which splits a region * as soon as any of its store files exceeds a maximum configurable * size. + *

This is the default split policy.

*/ -class ConstantSizeRegionSplitPolicy extends RegionSplitPolicy { +public class ConstantSizeRegionSplitPolicy extends RegionSplitPolicy { private long desiredMaxFileSize; @Override - void configureForRegion(HRegion region) { + protected void configureForRegion(HRegion region) { super.configureForRegion(region); long maxFileSize = region.getTableDesc().getMaxFileSize(); @@ -41,7 +42,7 @@ } @Override - boolean shouldSplit() { + protected boolean shouldSplit() { boolean force = region.shouldForceSplit(); boolean foundABigStore = false; Index: src/main/java/org/apache/hadoop/hbase/regionserver/RegionSplitPolicy.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/RegionSplitPolicy.java (revision 1238830) +++ src/main/java/org/apache/hadoop/hbase/regionserver/RegionSplitPolicy.java (working copy) @@ -32,7 +32,7 @@ * A split policy determines when a region should be split. * {@see ConstantSizeRegionSplitPolicy} */ -abstract class RegionSplitPolicy extends Configured { +public abstract class RegionSplitPolicy extends Configured { private static final Class DEFAULT_SPLIT_POLICY_CLASS = ConstantSizeRegionSplitPolicy.class; @@ -45,7 +45,7 @@ * Upon construction, this method will be called with the region * to be governed. It will be called once and only once. */ - void configureForRegion(HRegion region) { + protected void configureForRegion(HRegion region) { Preconditions.checkState( this.region == null, "Policy already configured for region {}", @@ -57,14 +57,17 @@ /** * @return true if the specified region should be split. */ - abstract boolean shouldSplit(); + protected abstract boolean shouldSplit(); /** * @return the key at which the region should be split, or null * if it cannot be split. This will only be called if shouldSplit * previously returned true. */ - byte[] getSplitPoint() { + protected byte[] getSplitPoint() { + if (this.region.getExplicitSplitPoint() != null) { + return this.region.getExplicitSplitPoint(); + } Map stores = region.getStores(); byte[] splitPointFromLargestStore = null; Index: src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java (revision 1238830) +++ src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java (working copy) @@ -4863,10 +4863,6 @@ return null; } - if (this.explicitSplitPoint != null) { - return this.explicitSplitPoint; - } - if (!splitPolicy.shouldSplit()) { return null; }