PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/src/test/java/org/apache/hadoop/hbase/util/TestRegionSplitCalculator.java

http://github.com/apache/hbase
Java | 358 lines | 282 code | 43 blank | 33 comment | 4 complexity | d36b6bb7b6b981c5f449c99c814c6799 MD5 | raw file
Possible License(s): Apache-2.0, MIT
  1. /**
  2. * Copyright 2011 The Apache Software Foundation
  3. *
  4. * Licensed to the Apache Software Foundation (ASF) under one
  5. * or more contributor license agreements. See the NOTICE file
  6. * distributed with this work for additional information
  7. * regarding copyright ownership. The ASF licenses this file
  8. * to you under the Apache License, Version 2.0 (the
  9. * "License"); you may not use this file except in compliance
  10. * with the License. You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. package org.apache.hadoop.hbase.util;
  21. import static org.junit.Assert.assertEquals;
  22. import java.util.Collection;
  23. import java.util.Comparator;
  24. import java.util.SortedSet;
  25. import org.apache.commons.logging.Log;
  26. import org.apache.commons.logging.LogFactory;
  27. import org.apache.hadoop.hbase.SmallTests;
  28. import org.junit.Test;
  29. import com.google.common.collect.ComparisonChain;
  30. import com.google.common.collect.Multimap;
  31. import org.junit.experimental.categories.Category;
  32. @Category(SmallTests.class)
  33. public class TestRegionSplitCalculator {
  34. final static Log LOG = LogFactory.getLog(TestRegionSplitCalculator.class);
  35. /**
  36. * This is range uses a user specified start and end keys. It also has an
  37. * extra time based tiebreaker so that different ranges with the same
  38. * start/end key pair count as different regions.
  39. */
  40. static class SimpleRange implements KeyRange {
  41. byte[] start, end;
  42. long tiebreaker;
  43. SimpleRange(byte[] start, byte[] end) {
  44. this.start = start;
  45. this.end = end;
  46. this.tiebreaker = System.nanoTime();
  47. }
  48. @Override
  49. public byte[] getStartKey() {
  50. return start;
  51. }
  52. @Override
  53. public byte[] getEndKey() {
  54. return end;
  55. }
  56. public String toString() {
  57. return "[" + Bytes.toString(start) + ", " + Bytes.toString(end) + "]";
  58. }
  59. }
  60. Comparator<SimpleRange> cmp = new Comparator<SimpleRange>() {
  61. @Override
  62. public int compare(SimpleRange sr1, SimpleRange sr2) {
  63. ComparisonChain cc = ComparisonChain.start();
  64. cc = cc.compare(sr1.getStartKey(), sr2.getStartKey(),
  65. Bytes.BYTES_COMPARATOR);
  66. cc = cc.compare(sr1.getEndKey(), sr2.getEndKey(),
  67. RegionSplitCalculator.BYTES_COMPARATOR);
  68. cc = cc.compare(sr1.tiebreaker, sr2.tiebreaker);
  69. return cc.result();
  70. }
  71. };
  72. /**
  73. * Check the "depth" (number of regions included at a split) of a generated
  74. * split calculation
  75. */
  76. void checkDepths(SortedSet<byte[]> splits,
  77. Multimap<byte[], SimpleRange> regions, Integer... depths) {
  78. assertEquals(splits.size(), depths.length);
  79. int i = 0;
  80. for (byte[] k : splits) {
  81. Collection<SimpleRange> rs = regions.get(k);
  82. int sz = rs == null ? 0 : rs.size();
  83. assertEquals((int) depths[i], sz);
  84. i++;
  85. }
  86. }
  87. /**
  88. * This dumps data in a visually reasonable way for visual debugging. It has
  89. * the basic iteration structure.
  90. */
  91. String dump(SortedSet<byte[]> splits, Multimap<byte[], SimpleRange> regions) {
  92. // we display this way because the last end key should be displayed as well.
  93. StringBuilder sb = new StringBuilder();
  94. for (byte[] k : splits) {
  95. sb.append(Bytes.toString(k) + ":\t");
  96. for (SimpleRange r : regions.get(k)) {
  97. sb.append(r.toString() + "\t");
  98. }
  99. sb.append("\n");
  100. }
  101. String s = sb.toString();
  102. LOG.info("\n" + s);
  103. return s;
  104. }
  105. @Test
  106. public void testSplitCalculator() {
  107. SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
  108. SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("C"));
  109. SimpleRange c = new SimpleRange(Bytes.toBytes("C"), Bytes.toBytes("D"));
  110. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  111. cmp);
  112. sc.add(a);
  113. sc.add(b);
  114. sc.add(c);
  115. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  116. LOG.info("Standard");
  117. String res = dump(sc.getSplits(), regions);
  118. checkDepths(sc.getSplits(), regions, 1, 1, 1, 0);
  119. assertEquals(res, "A:\t[A, B]\t\n" + "B:\t[B, C]\t\n" + "C:\t[C, D]\t\n"
  120. + "D:\t\n");
  121. }
  122. @Test
  123. public void testSplitCalculatorNoEdge() {
  124. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  125. cmp);
  126. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  127. LOG.info("Empty");
  128. String res = dump(sc.getSplits(), regions);
  129. checkDepths(sc.getSplits(), regions);
  130. assertEquals(res, "");
  131. }
  132. @Test
  133. public void testSplitCalculatorSingleEdge() {
  134. SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
  135. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  136. cmp);
  137. sc.add(a);
  138. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  139. LOG.info("Single edge");
  140. String res = dump(sc.getSplits(), regions);
  141. checkDepths(sc.getSplits(), regions, 1, 0);
  142. assertEquals(res, "A:\t[A, B]\t\n" + "B:\t\n");
  143. }
  144. @Test
  145. public void testSplitCalculatorDegenerateEdge() {
  146. SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("A"));
  147. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  148. cmp);
  149. sc.add(a);
  150. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  151. LOG.info("Single empty edge");
  152. String res = dump(sc.getSplits(), regions);
  153. checkDepths(sc.getSplits(), regions, 1);
  154. assertEquals(res, "A:\t[A, A]\t\n");
  155. }
  156. @Test
  157. public void testSplitCalculatorCoverSplit() {
  158. SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
  159. SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("C"));
  160. SimpleRange c = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
  161. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  162. cmp);
  163. sc.add(a);
  164. sc.add(b);
  165. sc.add(c);
  166. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  167. LOG.info("AC covers AB, BC");
  168. String res = dump(sc.getSplits(), regions);
  169. checkDepths(sc.getSplits(), regions, 2, 2, 0);
  170. assertEquals(res, "A:\t[A, B]\t[A, C]\t\n" + "B:\t[A, C]\t[B, C]\t\n"
  171. + "C:\t\n");
  172. }
  173. @Test
  174. public void testSplitCalculatorOverEndpoint() {
  175. SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
  176. SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("C"));
  177. SimpleRange c = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("D"));
  178. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  179. cmp);
  180. sc.add(a);
  181. sc.add(b);
  182. sc.add(c);
  183. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  184. LOG.info("AB, BD covers BC");
  185. String res = dump(sc.getSplits(), regions);
  186. checkDepths(sc.getSplits(), regions, 1, 2, 1, 0);
  187. assertEquals(res, "A:\t[A, B]\t\n" + "B:\t[B, C]\t[B, D]\t\n"
  188. + "C:\t[B, D]\t\n" + "D:\t\n");
  189. }
  190. @Test
  191. public void testSplitCalculatorHoles() {
  192. SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
  193. SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("C"));
  194. SimpleRange c = new SimpleRange(Bytes.toBytes("E"), Bytes.toBytes("F"));
  195. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  196. cmp);
  197. sc.add(a);
  198. sc.add(b);
  199. sc.add(c);
  200. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  201. LOG.info("Hole between C and E");
  202. String res = dump(sc.getSplits(), regions);
  203. checkDepths(sc.getSplits(), regions, 1, 1, 0, 1, 0);
  204. assertEquals(res, "A:\t[A, B]\t\n" + "B:\t[B, C]\t\n" + "C:\t\n"
  205. + "E:\t[E, F]\t\n" + "F:\t\n");
  206. }
  207. @Test
  208. public void testSplitCalculatorOverreach() {
  209. SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
  210. SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("D"));
  211. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  212. cmp);
  213. sc.add(a);
  214. sc.add(b);
  215. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  216. LOG.info("AC and BD overlap but share no start/end keys");
  217. String res = dump(sc.getSplits(), regions);
  218. checkDepths(sc.getSplits(), regions, 1, 2, 1, 0);
  219. assertEquals(res, "A:\t[A, C]\t\n" + "B:\t[A, C]\t[B, D]\t\n"
  220. + "C:\t[B, D]\t\n" + "D:\t\n");
  221. }
  222. @Test
  223. public void testSplitCalculatorFloor() {
  224. SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
  225. SimpleRange b = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B"));
  226. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  227. cmp);
  228. sc.add(a);
  229. sc.add(b);
  230. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  231. LOG.info("AC and AB overlap in the beginning");
  232. String res = dump(sc.getSplits(), regions);
  233. checkDepths(sc.getSplits(), regions, 2, 1, 0);
  234. assertEquals(res, "A:\t[A, B]\t[A, C]\t\n" + "B:\t[A, C]\t\n" + "C:\t\n");
  235. }
  236. @Test
  237. public void testSplitCalculatorCeil() {
  238. SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
  239. SimpleRange b = new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("C"));
  240. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  241. cmp);
  242. sc.add(a);
  243. sc.add(b);
  244. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  245. LOG.info("AC and BC overlap in the end");
  246. String res = dump(sc.getSplits(), regions);
  247. checkDepths(sc.getSplits(), regions, 1, 2, 0);
  248. assertEquals(res, "A:\t[A, C]\t\n" + "B:\t[A, C]\t[B, C]\t\n" + "C:\t\n");
  249. }
  250. @Test
  251. public void testSplitCalculatorEq() {
  252. SimpleRange a = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
  253. SimpleRange b = new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C"));
  254. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  255. cmp);
  256. sc.add(a);
  257. sc.add(b);
  258. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  259. LOG.info("AC and AC overlap completely");
  260. String res = dump(sc.getSplits(), regions);
  261. checkDepths(sc.getSplits(), regions, 2, 0);
  262. assertEquals(res, "A:\t[A, C]\t[A, C]\t\n" + "C:\t\n");
  263. }
  264. @Test
  265. public void testSplitCalculatorBackwards() {
  266. SimpleRange a = new SimpleRange(Bytes.toBytes("C"), Bytes.toBytes("A"));
  267. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  268. cmp);
  269. sc.add(a);
  270. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  271. LOG.info("CA is backwards");
  272. String res = dump(sc.getSplits(), regions);
  273. checkDepths(sc.getSplits(), regions); // expect nothing
  274. assertEquals(res, "");
  275. }
  276. @Test
  277. public void testComplex() {
  278. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  279. cmp);
  280. sc.add(new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("Am")));
  281. sc.add(new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("C")));
  282. sc.add(new SimpleRange(Bytes.toBytes("Am"), Bytes.toBytes("C")));
  283. sc.add(new SimpleRange(Bytes.toBytes("D"), Bytes.toBytes("E")));
  284. sc.add(new SimpleRange(Bytes.toBytes("F"), Bytes.toBytes("G")));
  285. sc.add(new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("E")));
  286. sc.add(new SimpleRange(Bytes.toBytes("H"), Bytes.toBytes("I")));
  287. sc.add(new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B")));
  288. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  289. LOG.info("Something fairly complex");
  290. String res = dump(sc.getSplits(), regions);
  291. checkDepths(sc.getSplits(), regions, 3, 3, 3, 1, 2, 0, 1, 0, 1, 0);
  292. assertEquals(res, "A:\t[A, Am]\t[A, B]\t[A, C]\t\n"
  293. + "Am:\t[A, B]\t[A, C]\t[Am, C]\t\n"
  294. + "B:\t[A, C]\t[Am, C]\t[B, E]\t\n" + "C:\t[B, E]\t\n"
  295. + "D:\t[B, E]\t[D, E]\t\n" + "E:\t\n" + "F:\t[F, G]\t\n" + "G:\t\n"
  296. + "H:\t[H, I]\t\n" + "I:\t\n");
  297. }
  298. @Test
  299. public void testBeginEndMarker() {
  300. RegionSplitCalculator<SimpleRange> sc = new RegionSplitCalculator<SimpleRange>(
  301. cmp);
  302. sc.add(new SimpleRange(Bytes.toBytes(""), Bytes.toBytes("A")));
  303. sc.add(new SimpleRange(Bytes.toBytes("A"), Bytes.toBytes("B")));
  304. sc.add(new SimpleRange(Bytes.toBytes("B"), Bytes.toBytes("")));
  305. Multimap<byte[], SimpleRange> regions = sc.calcCoverage();
  306. LOG.info("Special cases -- empty");
  307. String res = dump(sc.getSplits(), regions);
  308. checkDepths(sc.getSplits(), regions, 1, 1, 1, 0);
  309. assertEquals(res, ":\t[, A]\t\n" + "A:\t[A, B]\t\n" + "B:\t[B, ]\t\n"
  310. + "null:\t\n");
  311. }
  312. @org.junit.Rule
  313. public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
  314. new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
  315. }