1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.util;
21
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertTrue;
24
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.Comparator;
28 import java.util.List;
29 import java.util.SortedSet;
30 import java.util.UUID;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.hadoop.hbase.SmallTests;
35 import org.junit.Test;
36
37 import com.google.common.collect.ComparisonChain;
38 import com.google.common.collect.Multimap;
39
40 import org.junit.experimental.categories.Category;
41
42 @Category(SmallTests.class)
43 public class TestRegionSplitCalculator {
44 private static final Log LOG = LogFactory.getLog(TestRegionSplitCalculator.class);
45
46
47
48
49
50
51 static class SimpleRange implements KeyRange {
52 byte[] start, end;
53 UUID tiebreaker;
54
55 SimpleRange(byte[] start, byte[] end) {
56 this.start = start;
57 this.end = end;
58 this.tiebreaker = UUID.randomUUID();
59 }
60
61 @Override
62 public byte[] getStartKey() {
63 return start;
64 }
65
66 @Override
67 public byte[] getEndKey() {
68 return end;
69 }
70
71 public String toString() {
72 return "[" + Bytes.toString(start) + ", " + Bytes.toString(end) + "]";
73 }
74 }
75
76 Comparator<SimpleRange> cmp = new Comparator<SimpleRange>() {
77 @Override
78 public int compare(SimpleRange sr1, SimpleRange sr2) {
79 ComparisonChain cc = ComparisonChain.start();
80 cc = cc.compare(sr1.getStartKey(), sr2.getStartKey(),
81 Bytes.BYTES_COMPARATOR);
82 cc = cc.compare(sr1.getEndKey(), sr2.getEndKey(),
83 RegionSplitCalculator.BYTES_COMPARATOR);
84 cc = cc.compare(sr1.tiebreaker, sr2.tiebreaker);
85 return cc.result();
86 }
87 };
88
89
90
91
92
93 void checkDepths(SortedSet<byte[]> splits,
94 Multimap<byte[], SimpleRange> regions, Integer... depths) {
95 assertEquals(splits.size(), depths.length);
96 int i = 0;
97 for (byte[] k : splits) {
98 Collection<SimpleRange> rs = regions.get(k);
99 int sz = rs == null ? 0 : rs.size();
100 assertEquals((int) depths[i], sz);
101 i++;
102 }
103 }
104
105
106
107
108
109 String dump(SortedSet<byte[]> splits, Multimap<byte[], SimpleRange> regions) {
110
111 StringBuilder sb = new StringBuilder();
112 for (byte[] k : splits) {
113 sb.append(Bytes.toString(k)).append(":\t");
114 for (SimpleRange r : regions.get(k)) {
115 sb.append(r.toString()).append("\t");
116 }
117 sb.append("\n");
118 }
119 String s = sb.toString();
120 LOG.info("\n" + s);
121 return s;
122 }
123
124 @Test
125 public void testSplitCalculator() {
126 SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
127 SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("C"));
128 SimpleRange c = new SimpleRange(Bytes.toBytes("C"), Bytes.toBytes("D"));
129 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
130 cmp);
131 sc.add(a);
132 sc.add(b);
133 sc.add(c);
134
135 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
136 LOG.info("Standard");
137 String res = dump(sc.getSplits(), regions);
138 checkDepths(sc.getSplits(), regions, 1, 1, 1, 0);
139 assertEquals(res, "A:\t[A, B]\t\n" + "B:\t[B, C]\t\n" + "C:\t[C, D]\t\n"
140 + "D:\t\n");
141 }
142
143 @Test
144 public void testSplitCalculatorNoEdge() {
145 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
146 cmp);
147
148 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
149 LOG.info("Empty");
150 String res = dump(sc.getSplits(), regions);
151 checkDepths(sc.getSplits(), regions);
152 assertEquals("", res);
153 }
154
155 @Test
156 public void testSplitCalculatorSingleEdge() {
157 SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
158 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
159 cmp);
160 sc.add(a);
161
162 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
163 LOG.info("Single edge");
164 String res = dump(sc.getSplits(), regions);
165 checkDepths(sc.getSplits(), regions, 1, 0);
166 assertEquals("A:\t[A, B]\t\n" + "B:\t\n", res);
167 }
168
169 @Test
170 public void testSplitCalculatorDegenerateEdge() {
171 SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("A"));
172 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
173 cmp);
174 sc.add(a);
175
176 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
177 LOG.info("Single empty edge");
178 String res = dump(sc.getSplits(), regions);
179 checkDepths(sc.getSplits(), regions, 1);
180 assertEquals("A:\t[A, A]\t\n", res);
181 }
182
183 @Test
184 public void testSplitCalculatorCoverSplit() {
185 SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
186 SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("C"));
187 SimpleRange c = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
188 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
189 cmp);
190 sc.add(a);
191 sc.add(b);
192 sc.add(c);
193
194 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
195 LOG.info("AC covers AB, BC");
196 String res = dump(sc.getSplits(), regions);
197 checkDepths(sc.getSplits(), regions, 2, 2, 0);
198 assertEquals("A:\t[A, B]\t[A, C]\t\n" + "B:\t[A, C]\t[B, C]\t\n"
199 + "C:\t\n", res);
200 }
201
202 @Test
203 public void testSplitCalculatorOverEndpoint() {
204 SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
205 SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("C"));
206 SimpleRange c = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("D"));
207 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
208 cmp);
209 sc.add(a);
210 sc.add(b);
211 sc.add(c);
212
213 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
214 LOG.info("AB, BD covers BC");
215 String res = dump(sc.getSplits(), regions);
216 checkDepths(sc.getSplits(), regions, 1, 2, 1, 0);
217 assertEquals("A:\t[A, B]\t\n" + "B:\t[B, C]\t[B, D]\t\n"
218 + "C:\t[B, D]\t\n" + "D:\t\n", res);
219 }
220
221 @Test
222 public void testSplitCalculatorHoles() {
223 SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
224 SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("C"));
225 SimpleRange c = new SimpleRange(Bytes.toBytes("E"), Bytes.toBytes("F"));
226 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
227 cmp);
228 sc.add(a);
229 sc.add(b);
230 sc.add(c);
231
232 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
233 LOG.info("Hole between C and E");
234 String res = dump(sc.getSplits(), regions);
235 checkDepths(sc.getSplits(), regions, 1, 1, 0, 1, 0);
236 assertEquals("A:\t[A, B]\t\n" + "B:\t[B, C]\t\n" + "C:\t\n"
237 + "E:\t[E, F]\t\n" + "F:\t\n", res);
238 }
239
240 @Test
241 public void testSplitCalculatorOverreach() {
242 SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
243 SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("D"));
244 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
245 cmp);
246 sc.add(a);
247 sc.add(b);
248
249 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
250 LOG.info("AC and BD overlap but share no start/end keys");
251 String res = dump(sc.getSplits(), regions);
252 checkDepths(sc.getSplits(), regions, 1, 2, 1, 0);
253 assertEquals("A:\t[A, C]\t\n" + "B:\t[A, C]\t[B, D]\t\n"
254 + "C:\t[B, D]\t\n" + "D:\t\n", res);
255 }
256
257 @Test
258 public void testSplitCalculatorFloor() {
259 SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
260 SimpleRange b = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
261 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
262 cmp);
263 sc.add(a);
264 sc.add(b);
265
266 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
267 LOG.info("AC and AB overlap in the beginning");
268 String res = dump(sc.getSplits(), regions);
269 checkDepths(sc.getSplits(), regions, 2, 1, 0);
270 assertEquals("A:\t[A, B]\t[A, C]\t\n" + "B:\t[A, C]\t\n" + "C:\t\n", res);
271 }
272
273 @Test
274 public void testSplitCalculatorCeil() {
275 SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
276 SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("C"));
277 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
278 cmp);
279 sc.add(a);
280 sc.add(b);
281
282 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
283 LOG.info("AC and BC overlap in the end");
284 String res = dump(sc.getSplits(), regions);
285 checkDepths(sc.getSplits(), regions, 1, 2, 0);
286 assertEquals("A:\t[A, C]\t\n" + "B:\t[A, C]\t[B, C]\t\n" + "C:\t\n", res);
287 }
288
289 @Test
290 public void testSplitCalculatorEq() {
291 SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
292 SimpleRange b = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
293
294 LOG.info(a.tiebreaker + " - " + b.tiebreaker);
295 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
296 cmp);
297 sc.add(a);
298 sc.add(b);
299
300 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
301 LOG.info("AC and AC overlap completely");
302 String res = dump(sc.getSplits(), regions);
303 checkDepths(sc.getSplits(), regions, 2, 0);
304 assertEquals("A:\t[A, C]\t[A, C]\t\n" + "C:\t\n", res);
305 }
306
307 @Test
308 public void testSplitCalculatorBackwards() {
309 SimpleRange a = new SimpleRange(Bytes.toBytes("C"), Bytes.toBytes("A"));
310 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
311 cmp);
312 sc.add(a);
313
314 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
315 LOG.info("CA is backwards");
316 String res = dump(sc.getSplits(), regions);
317 checkDepths(sc.getSplits(), regions);
318 assertEquals("", res);
319 }
320
321 @Test
322 public void testComplex() {
323 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
324 cmp);
325 sc.add(new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("Am")));
326 sc.add(new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C")));
327 sc.add(new SimpleRange(Bytes.toBytes("Am"), Bytes.toBytes("C")));
328 sc.add(new SimpleRange(Bytes.toBytes("D"), Bytes.toBytes("E")));
329 sc.add(new SimpleRange(Bytes.toBytes("F"), Bytes.toBytes("G")));
330 sc.add(new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("E")));
331 sc.add(new SimpleRange(Bytes.toBytes("H"), Bytes.toBytes("I")));
332 sc.add(new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B")));
333
334 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
335 LOG.info("Something fairly complex");
336 String res = dump(sc.getSplits(), regions);
337 checkDepths(sc.getSplits(), regions, 3, 3, 3, 1, 2, 0, 1, 0, 1, 0);
338 assertEquals("A:\t[A, Am]\t[A, B]\t[A, C]\t\n"
339 + "Am:\t[A, B]\t[A, C]\t[Am, C]\t\n"
340 + "B:\t[A, C]\t[Am, C]\t[B, E]\t\n" + "C:\t[B, E]\t\n"
341 + "D:\t[B, E]\t[D, E]\t\n" + "E:\t\n" + "F:\t[F, G]\t\n" + "G:\t\n"
342 + "H:\t[H, I]\t\n" + "I:\t\n", res);
343 }
344
345 @Test
346 public void testBeginEndMarker() {
347 RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
348 cmp);
349 sc.add(new SimpleRange(Bytes.toBytes(""), Bytes.toBytes("A")));
350 sc.add(new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B")));
351 sc.add(new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("")));
352
353 Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
354 LOG.info("Special cases -- empty");
355 String res = dump(sc.getSplits(), regions);
356 checkDepths(sc.getSplits(), regions, 1, 1, 1, 0);
357 assertEquals(":\t[, A]\t\n" + "A:\t[A, B]\t\n" + "B:\t[B, ]\t\n"
358 + "null:\t\n", res);
359 }
360
361 @Test
362 public void testBigRanges() {
363 SimpleRange ai = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("I"));
364 SimpleRange ae = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("E"));
365 SimpleRange ac = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
366
367 Collection<SimpleRange> bigOverlap = new ArrayList<SimpleRange>();
368 bigOverlap.add(new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("E")));
369 bigOverlap.add(new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C")));
370 bigOverlap.add(new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B")));
371 bigOverlap.add(new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("C")));
372 bigOverlap.add(new SimpleRange(Bytes.toBytes("E"), Bytes.toBytes("H")));
373 bigOverlap.add(ai);
374 bigOverlap.add(ae);
375 bigOverlap.add(ac);
376
377
378 List<SimpleRange> bigRanges = RegionSplitCalculator.findBigRanges(bigOverlap, 1);
379 assertEquals(1, bigRanges.size());
380 assertEquals(ai, bigRanges.get(0));
381
382
383 bigRanges = RegionSplitCalculator.findBigRanges(bigOverlap, 3);
384 assertEquals(3, bigRanges.size());
385 assertEquals(ai, bigRanges.get(0));
386
387 SimpleRange r1 = bigRanges.get(1);
388 SimpleRange r2 = bigRanges.get(2);
389 assertEquals("A", Bytes.toString(r1.start));
390 assertEquals("A", Bytes.toString(r2.start));
391 String r1e = Bytes.toString(r1.end);
392 String r2e = Bytes.toString(r2.end);
393 assertTrue((r1e.equals("C") && r2e.equals("E"))
394 || (r1e.equals("E") && r2e.equals("C")));
395 }
396
397 @org.junit.Rule
398 public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
399 new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
400 }
401