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