2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
.util
;
20 import static org
.junit
.Assert
.assertArrayEquals
;
21 import static org
.junit
.Assert
.assertEquals
;
22 import static org
.junit
.Assert
.assertFalse
;
23 import static org
.junit
.Assert
.assertNotSame
;
24 import static org
.junit
.Assert
.assertTrue
;
26 import java
.util
.ArrayList
;
27 import java
.util
.List
;
28 import org
.apache
.commons
.lang3
.ArrayUtils
;
29 import org
.apache
.hadoop
.conf
.Configuration
;
30 import org
.apache
.hadoop
.hbase
.HBaseClassTestRule
;
31 import org
.apache
.hadoop
.hbase
.HBaseTestingUtility
;
32 import org
.apache
.hadoop
.hbase
.HRegionLocation
;
33 import org
.apache
.hadoop
.hbase
.TableName
;
34 import org
.apache
.hadoop
.hbase
.client
.RegionInfo
;
35 import org
.apache
.hadoop
.hbase
.client
.RegionLocator
;
36 import org
.apache
.hadoop
.hbase
.client
.Table
;
37 import org
.apache
.hadoop
.hbase
.testclassification
.MediumTests
;
38 import org
.apache
.hadoop
.hbase
.testclassification
.MiscTests
;
39 import org
.apache
.hadoop
.hbase
.util
.RegionSplitter
.DecimalStringSplit
;
40 import org
.apache
.hadoop
.hbase
.util
.RegionSplitter
.HexStringSplit
;
41 import org
.apache
.hadoop
.hbase
.util
.RegionSplitter
.SplitAlgorithm
;
42 import org
.apache
.hadoop
.hbase
.util
.RegionSplitter
.UniformSplit
;
43 import org
.junit
.AfterClass
;
44 import org
.junit
.BeforeClass
;
45 import org
.junit
.ClassRule
;
46 import org
.junit
.Rule
;
47 import org
.junit
.Test
;
48 import org
.junit
.experimental
.categories
.Category
;
49 import org
.junit
.rules
.TestName
;
50 import org
.slf4j
.Logger
;
51 import org
.slf4j
.LoggerFactory
;
54 * Tests for {@link RegionSplitter}, which can create a pre-split table or do a
55 * rolling split of an existing table.
57 @Category({MiscTests
.class, MediumTests
.class})
58 public class TestRegionSplitter
{
61 public static final HBaseClassTestRule CLASS_RULE
=
62 HBaseClassTestRule
.forClass(TestRegionSplitter
.class);
64 private final static Logger LOG
= LoggerFactory
.getLogger(TestRegionSplitter
.class);
65 private final static HBaseTestingUtility UTIL
= new HBaseTestingUtility();
66 private final static String CF_NAME
= "SPLIT_TEST_CF";
67 private final static byte xFF
= (byte) 0xff;
70 public TestName name
= new TestName();
73 public static void setup() throws Exception
{
74 UTIL
.startMiniCluster();
78 public static void teardown() throws Exception
{
79 UTIL
.shutdownMiniCluster();
83 * Test creating a pre-split table using the HexStringSplit algorithm.
86 public void testCreatePresplitTableHex() throws Exception
{
87 final List
<byte[]> expectedBounds
= new ArrayList
<>(17);
88 expectedBounds
.add(ArrayUtils
.EMPTY_BYTE_ARRAY
);
89 expectedBounds
.add(Bytes
.toBytes("10000000"));
90 expectedBounds
.add(Bytes
.toBytes("20000000"));
91 expectedBounds
.add(Bytes
.toBytes("30000000"));
92 expectedBounds
.add(Bytes
.toBytes("40000000"));
93 expectedBounds
.add(Bytes
.toBytes("50000000"));
94 expectedBounds
.add(Bytes
.toBytes("60000000"));
95 expectedBounds
.add(Bytes
.toBytes("70000000"));
96 expectedBounds
.add(Bytes
.toBytes("80000000"));
97 expectedBounds
.add(Bytes
.toBytes("90000000"));
98 expectedBounds
.add(Bytes
.toBytes("a0000000"));
99 expectedBounds
.add(Bytes
.toBytes("b0000000"));
100 expectedBounds
.add(Bytes
.toBytes("c0000000"));
101 expectedBounds
.add(Bytes
.toBytes("d0000000"));
102 expectedBounds
.add(Bytes
.toBytes("e0000000"));
103 expectedBounds
.add(Bytes
.toBytes("f0000000"));
104 expectedBounds
.add(ArrayUtils
.EMPTY_BYTE_ARRAY
);
106 // Do table creation/pre-splitting and verification of region boundaries
107 preSplitTableAndVerify(expectedBounds
,
108 HexStringSplit
.class.getSimpleName(),
109 TableName
.valueOf(name
.getMethodName()));
113 * Test creating a pre-split table using the UniformSplit algorithm.
116 public void testCreatePresplitTableUniform() throws Exception
{
117 List
<byte[]> expectedBounds
= new ArrayList
<>(17);
118 expectedBounds
.add(ArrayUtils
.EMPTY_BYTE_ARRAY
);
119 expectedBounds
.add(new byte[] { 0x10, 0, 0, 0, 0, 0, 0, 0});
120 expectedBounds
.add(new byte[] { 0x20, 0, 0, 0, 0, 0, 0, 0});
121 expectedBounds
.add(new byte[] { 0x30, 0, 0, 0, 0, 0, 0, 0});
122 expectedBounds
.add(new byte[] { 0x40, 0, 0, 0, 0, 0, 0, 0});
123 expectedBounds
.add(new byte[] { 0x50, 0, 0, 0, 0, 0, 0, 0 });
124 expectedBounds
.add(new byte[] { 0x60, 0, 0, 0, 0, 0, 0, 0 });
125 expectedBounds
.add(new byte[] { 0x70, 0, 0, 0, 0, 0, 0, 0 });
126 expectedBounds
.add(new byte[] { (byte) 0x80, 0, 0, 0, 0, 0, 0, 0 });
127 expectedBounds
.add(new byte[] { (byte) 0x90, 0, 0, 0, 0, 0, 0, 0 });
128 expectedBounds
.add(new byte[] {(byte)0xa0, 0, 0, 0, 0, 0, 0, 0});
129 expectedBounds
.add(new byte[] { (byte) 0xb0, 0, 0, 0, 0, 0, 0, 0 });
130 expectedBounds
.add(new byte[] { (byte) 0xc0, 0, 0, 0, 0, 0, 0, 0 });
131 expectedBounds
.add(new byte[] { (byte) 0xd0, 0, 0, 0, 0, 0, 0, 0 });
132 expectedBounds
.add(new byte[] {(byte)0xe0, 0, 0, 0, 0, 0, 0, 0});
133 expectedBounds
.add(new byte[] { (byte) 0xf0, 0, 0, 0, 0, 0, 0, 0 });
134 expectedBounds
.add(ArrayUtils
.EMPTY_BYTE_ARRAY
);
136 // Do table creation/pre-splitting and verification of region boundaries
137 preSplitTableAndVerify(expectedBounds
, UniformSplit
.class.getSimpleName(),
138 TableName
.valueOf(name
.getMethodName()));
142 * Unit tests for the HexStringSplit algorithm. Makes sure it divides up the
143 * space of keys in the way that we expect.
146 public void unitTestHexStringSplit() {
147 HexStringSplit splitter
= new HexStringSplit();
148 // Check splitting while starting from scratch
150 byte[][] twoRegionsSplits
= splitter
.split(2);
151 assertEquals(1, twoRegionsSplits
.length
);
152 assertArrayEquals(Bytes
.toBytes("80000000"), twoRegionsSplits
[0]);
154 byte[][] threeRegionsSplits
= splitter
.split(3);
155 assertEquals(2, threeRegionsSplits
.length
);
156 byte[] expectedSplit0
= Bytes
.toBytes("55555555");
157 assertArrayEquals(expectedSplit0
, threeRegionsSplits
[0]);
158 byte[] expectedSplit1
= Bytes
.toBytes("aaaaaaaa");
159 assertArrayEquals(expectedSplit1
, threeRegionsSplits
[1]);
161 // Check splitting existing regions that have start and end points
162 byte[] splitPoint
= splitter
.split(Bytes
.toBytes("10000000"), Bytes
.toBytes("30000000"));
163 assertArrayEquals(Bytes
.toBytes("20000000"), splitPoint
);
165 byte[] lastRow
= Bytes
.toBytes("ffffffff");
166 assertArrayEquals(lastRow
, splitter
.lastRow());
167 byte[] firstRow
= Bytes
.toBytes("00000000");
168 assertArrayEquals(firstRow
, splitter
.firstRow());
170 // Halfway between 00... and 20... should be 10...
171 splitPoint
= splitter
.split(firstRow
, Bytes
.toBytes("20000000"));
172 assertArrayEquals(Bytes
.toBytes("10000000"), splitPoint
);
174 // Halfway between df... and ff... should be ef....
175 splitPoint
= splitter
.split(Bytes
.toBytes("dfffffff"), lastRow
);
176 assertArrayEquals(Bytes
.toBytes("efffffff"), splitPoint
);
178 // Check splitting region with multiple mappers per region
179 byte[][] splits
= splitter
.split(Bytes
.toBytes("00000000"), Bytes
.toBytes("30000000"),
181 assertEquals(2, splits
.length
);
182 assertArrayEquals(Bytes
.toBytes("10000000"), splits
[0]);
183 assertArrayEquals(Bytes
.toBytes("20000000"), splits
[1]);
185 splits
= splitter
.split(Bytes
.toBytes("00000000"), Bytes
.toBytes("20000000"), 2, true);
186 assertEquals(3, splits
.length
);
187 assertArrayEquals(Bytes
.toBytes("10000000"), splits
[1]);
191 * Unit tests for the DecimalStringSplit algorithm. Makes sure it divides up the
192 * space of keys in the way that we expect.
195 public void unitTestDecimalStringSplit() {
196 DecimalStringSplit splitter
= new DecimalStringSplit();
197 // Check splitting while starting from scratch
199 byte[][] twoRegionsSplits
= splitter
.split(2);
200 assertEquals(1, twoRegionsSplits
.length
);
201 assertArrayEquals(Bytes
.toBytes("50000000"), twoRegionsSplits
[0]);
203 byte[][] threeRegionsSplits
= splitter
.split(3);
204 assertEquals(2, threeRegionsSplits
.length
);
205 byte[] expectedSplit0
= Bytes
.toBytes("33333333");
206 assertArrayEquals(expectedSplit0
, threeRegionsSplits
[0]);
207 byte[] expectedSplit1
= Bytes
.toBytes("66666666");
208 assertArrayEquals(expectedSplit1
, threeRegionsSplits
[1]);
210 // Check splitting existing regions that have start and end points
211 byte[] splitPoint
= splitter
.split(Bytes
.toBytes("10000000"), Bytes
.toBytes("30000000"));
212 assertArrayEquals(Bytes
.toBytes("20000000"), splitPoint
);
214 byte[] lastRow
= Bytes
.toBytes("99999999");
215 assertArrayEquals(lastRow
, splitter
.lastRow());
216 byte[] firstRow
= Bytes
.toBytes("00000000");
217 assertArrayEquals(firstRow
, splitter
.firstRow());
219 // Halfway between 00... and 20... should be 10...
220 splitPoint
= splitter
.split(firstRow
, Bytes
.toBytes("20000000"));
221 assertArrayEquals(Bytes
.toBytes("10000000"), splitPoint
);
223 // Halfway between 00... and 19... should be 09...
224 splitPoint
= splitter
.split(firstRow
, Bytes
.toBytes("19999999"));
225 assertArrayEquals(Bytes
.toBytes("09999999"), splitPoint
);
227 // Halfway between 79... and 99... should be 89....
228 splitPoint
= splitter
.split(Bytes
.toBytes("79999999"), lastRow
);
229 assertArrayEquals(Bytes
.toBytes("89999999"), splitPoint
);
231 // Check splitting region with multiple mappers per region
232 byte[][] splits
= splitter
.split(Bytes
.toBytes("00000000"), Bytes
.toBytes("30000000"),
234 assertEquals(2, splits
.length
);
235 assertArrayEquals(Bytes
.toBytes("10000000"), splits
[0]);
236 assertArrayEquals(Bytes
.toBytes("20000000"), splits
[1]);
238 splits
= splitter
.split(Bytes
.toBytes("00000000"), Bytes
.toBytes("20000000"), 2, true);
239 assertEquals(3, splits
.length
);
240 assertArrayEquals(Bytes
.toBytes("10000000"), splits
[1]);
244 * Unit tests for the UniformSplit algorithm. Makes sure it divides up the space of
245 * keys in the way that we expect.
248 public void unitTestUniformSplit() {
249 UniformSplit splitter
= new UniformSplit();
251 // Check splitting while starting from scratch
254 throw new AssertionError("Splitting into <2 regions should have thrown exception");
255 } catch (IllegalArgumentException e
) { }
257 byte[][] twoRegionsSplits
= splitter
.split(2);
258 assertEquals(1, twoRegionsSplits
.length
);
259 assertArrayEquals(twoRegionsSplits
[0], new byte[] { (byte) 0x80, 0, 0, 0, 0, 0, 0, 0 });
261 byte[][] threeRegionsSplits
= splitter
.split(3);
262 assertEquals(2, threeRegionsSplits
.length
);
263 byte[] expectedSplit0
= new byte[] {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
264 assertArrayEquals(expectedSplit0
, threeRegionsSplits
[0]);
265 byte[] expectedSplit1
= new byte[] {(byte)0xAA, (byte)0xAA, (byte)0xAA, (byte)0xAA,
266 (byte)0xAA, (byte)0xAA, (byte)0xAA, (byte)0xAA};
267 assertArrayEquals(expectedSplit1
, threeRegionsSplits
[1]);
269 // Check splitting existing regions that have start and end points
270 byte[] splitPoint
= splitter
.split(new byte[] {0x10}, new byte[] {0x30});
271 assertArrayEquals(new byte[] { 0x20 }, splitPoint
);
273 byte[] lastRow
= new byte[] {xFF
, xFF
, xFF
, xFF
, xFF
, xFF
, xFF
, xFF
};
274 assertArrayEquals(lastRow
, splitter
.lastRow());
275 byte[] firstRow
= ArrayUtils
.EMPTY_BYTE_ARRAY
;
276 assertArrayEquals(firstRow
, splitter
.firstRow());
278 splitPoint
= splitter
.split(firstRow
, new byte[] {0x20});
279 assertArrayEquals(splitPoint
, new byte[] { 0x10 });
281 splitPoint
= splitter
.split(new byte[] {(byte)0xdf, xFF
, xFF
, xFF
, xFF
,
282 xFF
, xFF
, xFF
}, lastRow
);
283 assertArrayEquals(splitPoint
, new byte[] { (byte) 0xef, xFF
, xFF
, xFF
, xFF
, xFF
, xFF
, xFF
});
285 splitPoint
= splitter
.split(new byte[] {'a', 'a', 'a'}, new byte[] {'a', 'a', 'b'});
286 assertArrayEquals(splitPoint
, new byte[] { 'a', 'a', 'a', (byte) 0x80 });
288 // Check splitting region with multiple mappers per region
289 byte[][] splits
= splitter
.split(new byte[] {'a', 'a', 'a'}, new byte[] {'a', 'a', 'd'},
291 assertEquals(2, splits
.length
);
292 assertArrayEquals(splits
[0], new byte[]{'a', 'a', 'b'});
293 assertArrayEquals(splits
[1], new byte[]{'a', 'a', 'c'});
295 splits
= splitter
.split(new byte[] {'a', 'a', 'a'}, new byte[] {'a', 'a', 'e'}, 2, true);
296 assertEquals(3, splits
.length
);
297 assertArrayEquals(splits
[1], new byte[] { 'a', 'a', 'c'});
301 public void testUserInput() {
302 SplitAlgorithm algo
= new HexStringSplit();
303 assertFalse(splitFailsPrecondition(algo
)); // default settings are fine
304 assertFalse(splitFailsPrecondition(algo
, "00", "AA")); // custom is fine
305 assertTrue(splitFailsPrecondition(algo
, "AA", "00")); // range error
306 assertTrue(splitFailsPrecondition(algo
, "AA", "AA")); // range error
307 assertFalse(splitFailsPrecondition(algo
, "0", "2", 3)); // should be fine
308 assertFalse(splitFailsPrecondition(algo
, "0", "A", 11)); // should be fine
309 assertTrue(splitFailsPrecondition(algo
, "0", "A", 12)); // too granular
311 algo
= new DecimalStringSplit();
312 assertFalse(splitFailsPrecondition(algo
)); // default settings are fine
313 assertFalse(splitFailsPrecondition(algo
, "00", "99")); // custom is fine
314 assertTrue(splitFailsPrecondition(algo
, "99", "00")); // range error
315 assertTrue(splitFailsPrecondition(algo
, "99", "99")); // range error
316 assertFalse(splitFailsPrecondition(algo
, "0", "2", 3)); // should be fine
317 assertFalse(splitFailsPrecondition(algo
, "0", "9", 10)); // should be fine
318 assertTrue(splitFailsPrecondition(algo
, "0", "9", 11)); // too granular
320 algo
= new UniformSplit();
321 assertFalse(splitFailsPrecondition(algo
)); // default settings are fine
322 assertFalse(splitFailsPrecondition(algo
, "\\x00", "\\xAA")); // custom is fine
323 assertTrue(splitFailsPrecondition(algo
, "\\xAA", "\\x00")); // range error
324 assertTrue(splitFailsPrecondition(algo
, "\\xAA", "\\xAA")); // range error
325 assertFalse(splitFailsPrecondition(algo
, "\\x00", "\\x02", 3)); // should be fine
326 assertFalse(splitFailsPrecondition(algo
, "\\x00", "\\x0A", 11)); // should be fine
327 assertFalse(splitFailsPrecondition(algo
, "\\x00", "\\x0A", 12)); // should be fine
330 private boolean splitFailsPrecondition(SplitAlgorithm algo
) {
331 return splitFailsPrecondition(algo
, 100);
334 private boolean splitFailsPrecondition(SplitAlgorithm algo
, String firstRow
,
336 return splitFailsPrecondition(algo
, firstRow
, lastRow
, 100);
339 private boolean splitFailsPrecondition(SplitAlgorithm algo
, String firstRow
,
340 String lastRow
, int numRegions
) {
341 algo
.setFirstRow(firstRow
);
342 algo
.setLastRow(lastRow
);
343 return splitFailsPrecondition(algo
, numRegions
);
346 private boolean splitFailsPrecondition(SplitAlgorithm algo
, int numRegions
) {
348 byte[][] s
= algo
.split(numRegions
);
349 LOG
.debug("split algo = " + algo
);
351 StringBuilder sb
= new StringBuilder();
353 sb
.append(Bytes
.toStringBinary(b
) + " ");
355 LOG
.debug(sb
.toString());
358 } catch (IllegalArgumentException e
) {
360 } catch (IllegalStateException e
) {
362 } catch (IndexOutOfBoundsException e
) {
368 * Creates a pre-split table with expectedBounds.size()+1 regions, then
369 * verifies that the region boundaries are the same as the expected
370 * region boundaries in expectedBounds.
371 * @throws Various junit assertions
373 private void preSplitTableAndVerify(List
<byte[]> expectedBounds
,
374 String splitClass
, TableName tableName
) throws Exception
{
375 final int numRegions
= expectedBounds
.size()-1;
376 final Configuration conf
= UTIL
.getConfiguration();
377 conf
.setInt("split.count", numRegions
);
378 SplitAlgorithm splitAlgo
= RegionSplitter
.newSplitAlgoInstance(conf
, splitClass
);
379 RegionSplitter
.createPresplitTable(tableName
, splitAlgo
, new String
[] { CF_NAME
}, conf
);
380 verifyBounds(expectedBounds
, tableName
);
384 public void noopRollingSplit() throws Exception
{
385 final List
<byte[]> expectedBounds
= new ArrayList
<>(1);
386 expectedBounds
.add(ArrayUtils
.EMPTY_BYTE_ARRAY
);
387 rollingSplitAndVerify(TableName
.valueOf(TestRegionSplitter
.class.getSimpleName()),
388 "UniformSplit", expectedBounds
);
391 private void rollingSplitAndVerify(TableName tableName
, String splitClass
,
392 List
<byte[]> expectedBounds
) throws Exception
{
393 final Configuration conf
= UTIL
.getConfiguration();
395 // Set this larger than the number of splits so RegionSplitter won't block
396 conf
.setInt("split.outstanding", 5);
397 SplitAlgorithm splitAlgo
= RegionSplitter
.newSplitAlgoInstance(conf
, splitClass
);
398 RegionSplitter
.rollingSplit(tableName
, splitAlgo
, conf
);
399 verifyBounds(expectedBounds
, tableName
);
402 private void verifyBounds(List
<byte[]> expectedBounds
, TableName tableName
)
404 // Get region boundaries from the cluster and verify their endpoints
405 final int numRegions
= expectedBounds
.size()-1;
406 try (Table table
= UTIL
.getConnection().getTable(tableName
);
407 RegionLocator locator
= UTIL
.getConnection().getRegionLocator(tableName
)) {
408 final List
<HRegionLocation
> regionInfoMap
= locator
.getAllRegionLocations();
409 assertEquals(numRegions
, regionInfoMap
.size());
410 for (HRegionLocation entry
: regionInfoMap
) {
411 final RegionInfo regionInfo
= entry
.getRegion();
412 byte[] regionStart
= regionInfo
.getStartKey();
413 byte[] regionEnd
= regionInfo
.getEndKey();
415 // This region's start key should be one of the region boundaries
416 int startBoundaryIndex
= indexOfBytes(expectedBounds
, regionStart
);
417 assertNotSame(-1, startBoundaryIndex
);
419 // This region's end key should be the region boundary that comes
420 // after the starting boundary.
421 byte[] expectedRegionEnd
= expectedBounds
.get(startBoundaryIndex
+ 1);
422 assertEquals(0, Bytes
.compareTo(regionEnd
, expectedRegionEnd
));
428 * List.indexOf() doesn't really work for a List<byte[]>, because byte[]
429 * doesn't override equals(). This method checks whether a list contains
430 * a given element by checking each element using the byte array comparator.
431 * @return the index of the first element that equals compareTo, or -1 if no elements are equal.
433 static private int indexOfBytes(List
<byte[]> list
, byte[] compareTo
) {
435 for(byte[] elem
: list
) {
436 if(Bytes
.BYTES_COMPARATOR
.compare(elem
, compareTo
) == 0) {