1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.util;
20
21 import java.io.IOException;
22 import java.math.BigInteger;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.TreeMap;
31
32 import org.apache.commons.cli.CommandLine;
33 import org.apache.commons.cli.GnuParser;
34 import org.apache.commons.cli.HelpFormatter;
35 import org.apache.commons.cli.OptionBuilder;
36 import org.apache.commons.cli.Options;
37 import org.apache.commons.cli.ParseException;
38 import org.apache.commons.lang.ArrayUtils;
39 import org.apache.commons.lang.StringUtils;
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42 import org.apache.hadoop.classification.InterfaceAudience;
43 import org.apache.hadoop.conf.Configuration;
44 import org.apache.hadoop.fs.FSDataInputStream;
45 import org.apache.hadoop.fs.FSDataOutputStream;
46 import org.apache.hadoop.fs.FileSystem;
47 import org.apache.hadoop.fs.Path;
48 import org.apache.hadoop.hbase.HBaseConfiguration;
49 import org.apache.hadoop.hbase.HColumnDescriptor;
50 import org.apache.hadoop.hbase.HConstants;
51 import org.apache.hadoop.hbase.HRegionInfo;
52 import org.apache.hadoop.hbase.HRegionLocation;
53 import org.apache.hadoop.hbase.HTableDescriptor;
54 import org.apache.hadoop.hbase.ServerName;
55 import org.apache.hadoop.hbase.catalog.MetaReader;
56 import org.apache.hadoop.hbase.client.HBaseAdmin;
57 import org.apache.hadoop.hbase.client.HTable;
58 import org.apache.hadoop.hbase.client.NoServerForRegionException;
59 import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
60
61 import com.google.common.base.Preconditions;
62 import com.google.common.collect.Lists;
63 import com.google.common.collect.Maps;
64 import com.google.common.collect.Sets;
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 @InterfaceAudience.Private
138 public class RegionSplitter {
139 static final Log LOG = LogFactory.getLog(RegionSplitter.class);
140
141
142
143
144
145
146
147
148
149
150 public static interface SplitAlgorithm {
151
152
153
154
155
156
157
158
159
160 byte[] split(byte[] start, byte[] end);
161
162
163
164
165
166
167
168
169
170
171
172
173
174 byte[][] split(int numRegions);
175
176
177
178
179
180
181
182
183 byte[] firstRow();
184
185
186
187
188
189
190
191
192 byte[] lastRow();
193
194
195
196
197
198
199
200
201
202 void setFirstRow(String userInput);
203
204
205
206
207
208
209
210
211
212
213 void setLastRow(String userInput);
214
215
216
217
218
219
220 byte[] strToRow(String input);
221
222
223
224
225
226
227 String rowToStr(byte[] row);
228
229
230
231
232 String separator();
233 }
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270 @SuppressWarnings("static-access")
271 public static void main(String[] args) throws IOException,
272 InterruptedException, ParseException {
273 Configuration conf = HBaseConfiguration.create();
274
275
276 Options opt = new Options();
277 opt.addOption(OptionBuilder.withArgName("property=value").hasArg()
278 .withDescription("Override HBase Configuration Settings").create("D"));
279 opt.addOption(OptionBuilder.withArgName("region count").hasArg()
280 .withDescription(
281 "Create a new table with a pre-split number of regions")
282 .create("c"));
283 opt.addOption(OptionBuilder.withArgName("family:family:...").hasArg()
284 .withDescription(
285 "Column Families to create with new table. Required with -c")
286 .create("f"));
287 opt.addOption("h", false, "Print this usage help");
288 opt.addOption("r", false, "Perform a rolling split of an existing region");
289 opt.addOption(OptionBuilder.withArgName("count").hasArg().withDescription(
290 "Max outstanding splits that have unfinished major compactions")
291 .create("o"));
292 opt.addOption(null, "firstrow", true,
293 "First Row in Table for Split Algorithm");
294 opt.addOption(null, "lastrow", true,
295 "Last Row in Table for Split Algorithm");
296 opt.addOption(null, "risky", false,
297 "Skip verification steps to complete quickly."
298 + "STRONGLY DISCOURAGED for production systems. ");
299 CommandLine cmd = new GnuParser().parse(opt, args);
300
301 if (cmd.hasOption("D")) {
302 for (String confOpt : cmd.getOptionValues("D")) {
303 String[] kv = confOpt.split("=", 2);
304 if (kv.length == 2) {
305 conf.set(kv[0], kv[1]);
306 LOG.debug("-D configuration override: " + kv[0] + "=" + kv[1]);
307 } else {
308 throw new ParseException("-D option format invalid: " + confOpt);
309 }
310 }
311 }
312
313 if (cmd.hasOption("risky")) {
314 conf.setBoolean("split.verify", false);
315 }
316
317 boolean createTable = cmd.hasOption("c") && cmd.hasOption("f");
318 boolean rollingSplit = cmd.hasOption("r");
319 boolean oneOperOnly = createTable ^ rollingSplit;
320
321 if (2 != cmd.getArgList().size() || !oneOperOnly || cmd.hasOption("h")) {
322 new HelpFormatter().printHelp("RegionSplitter <TABLE> <SPLITALGORITHM>\n"+
323 "SPLITALGORITHM is a java class name of a class implementing " +
324 "SplitAlgorithm, or one of the special strings HexStringSplit " +
325 "or UniformSplit, which are built-in split algorithms. " +
326 "HexStringSplit treats keys as hexadecimal ASCII, and " +
327 "UniformSplit treats keys as arbitrary bytes.", opt);
328 return;
329 }
330 String tableName = cmd.getArgs()[0];
331 String splitClass = cmd.getArgs()[1];
332 SplitAlgorithm splitAlgo = newSplitAlgoInstance(conf, splitClass);
333
334 if (cmd.hasOption("firstrow")) {
335 splitAlgo.setFirstRow(cmd.getOptionValue("firstrow"));
336 }
337 if (cmd.hasOption("lastrow")) {
338 splitAlgo.setLastRow(cmd.getOptionValue("lastrow"));
339 }
340
341 if (createTable) {
342 conf.set("split.count", cmd.getOptionValue("c"));
343 createPresplitTable(tableName, splitAlgo, cmd.getOptionValue("f").split(":"), conf);
344 }
345
346 if (rollingSplit) {
347 if (cmd.hasOption("o")) {
348 conf.set("split.outstanding", cmd.getOptionValue("o"));
349 }
350 rollingSplit(tableName, splitAlgo, conf);
351 }
352 }
353
354 static void createPresplitTable(String tableName, SplitAlgorithm splitAlgo,
355 String[] columnFamilies, Configuration conf) throws IOException,
356 InterruptedException {
357 final int splitCount = conf.getInt("split.count", 0);
358 Preconditions.checkArgument(splitCount > 1, "Split count must be > 1");
359
360 Preconditions.checkArgument(columnFamilies.length > 0,
361 "Must specify at least one column family. ");
362 LOG.debug("Creating table " + tableName + " with " + columnFamilies.length
363 + " column families. Presplitting to " + splitCount + " regions");
364
365 HTableDescriptor desc = new HTableDescriptor(tableName);
366 for (String cf : columnFamilies) {
367 desc.addFamily(new HColumnDescriptor(Bytes.toBytes(cf)));
368 }
369 HBaseAdmin admin = new HBaseAdmin(conf);
370 Preconditions.checkArgument(!admin.tableExists(tableName),
371 "Table already exists: " + tableName);
372 admin.createTable(desc, splitAlgo.split(splitCount));
373 admin.close();
374 LOG.debug("Table created! Waiting for regions to show online in META...");
375 if (!conf.getBoolean("split.verify", true)) {
376
377 int onlineRegions = 0;
378 while (onlineRegions < splitCount) {
379 onlineRegions = MetaReader.getRegionCount(conf, tableName);
380 LOG.debug(onlineRegions + " of " + splitCount + " regions online...");
381 if (onlineRegions < splitCount) {
382 Thread.sleep(10 * 1000);
383 }
384 }
385 }
386
387 LOG.debug("Finished creating table with " + splitCount + " regions");
388 }
389
390 static void rollingSplit(String tableName, SplitAlgorithm splitAlgo,
391 Configuration conf) throws IOException, InterruptedException {
392 final int minOS = conf.getInt("split.outstanding", 2);
393
394 HTable table = new HTable(conf, tableName);
395
396
397 final int MAX_OUTSTANDING =
398 Math.max(table.getConnection().getCurrentNrHRS() / 2, minOS);
399
400 Path hbDir = FSUtils.getRootDir(conf);
401 Path tableDir = HTableDescriptor.getTableDir(hbDir, table.getTableName());
402 Path splitFile = new Path(tableDir, "_balancedSplit");
403 FileSystem fs = FileSystem.get(conf);
404
405
406 LinkedList<Pair<byte[], byte[]>> tmpRegionSet = getSplits(table, splitAlgo);
407 LinkedList<Pair<byte[], byte[]>> outstanding = Lists.newLinkedList();
408 int splitCount = 0;
409 final int origCount = tmpRegionSet.size();
410
411
412
413
414 LOG.debug("Bucketing regions by regionserver...");
415 TreeMap<String, LinkedList<Pair<byte[], byte[]>>> daughterRegions =
416 Maps.newTreeMap();
417 for (Pair<byte[], byte[]> dr : tmpRegionSet) {
418 String rsLocation = table.getRegionLocation(dr.getSecond()).
419 getHostnamePort();
420 if (!daughterRegions.containsKey(rsLocation)) {
421 LinkedList<Pair<byte[], byte[]>> entry = Lists.newLinkedList();
422 daughterRegions.put(rsLocation, entry);
423 }
424 daughterRegions.get(rsLocation).add(dr);
425 }
426 LOG.debug("Done with bucketing. Split time!");
427 long startTime = System.currentTimeMillis();
428
429
430 FSDataInputStream tmpIn = fs.open(splitFile);
431 byte[] rawData = new byte[tmpIn.available()];
432 tmpIn.readFully(rawData);
433 tmpIn.close();
434 FSDataOutputStream splitOut = fs.create(splitFile);
435 splitOut.write(rawData);
436
437 try {
438
439 while (!daughterRegions.isEmpty()) {
440 LOG.debug(daughterRegions.size() + " RS have regions to splt.");
441
442
443 final TreeMap<ServerName, Integer> rsSizes = Maps.newTreeMap();
444 Map<HRegionInfo, ServerName> regionsInfo = table.getRegionLocations();
445 for (ServerName rs : regionsInfo.values()) {
446 if (rsSizes.containsKey(rs)) {
447 rsSizes.put(rs, rsSizes.get(rs) + 1);
448 } else {
449 rsSizes.put(rs, 1);
450 }
451 }
452
453
454 List<String> serversLeft = Lists.newArrayList(daughterRegions .keySet());
455 Collections.sort(serversLeft, new Comparator<String>() {
456 public int compare(String o1, String o2) {
457 return rsSizes.get(o1).compareTo(rsSizes.get(o2));
458 }
459 });
460
461
462
463 for (String rsLoc : serversLeft) {
464 Pair<byte[], byte[]> dr = null;
465
466
467 LOG.debug("Finding a region on " + rsLoc);
468 LinkedList<Pair<byte[], byte[]>> regionList = daughterRegions
469 .get(rsLoc);
470 while (!regionList.isEmpty()) {
471 dr = regionList.pop();
472
473
474 byte[] split = dr.getSecond();
475 HRegionLocation regionLoc = table.getRegionLocation(split);
476
477
478 String newRs = regionLoc.getHostnamePort();
479 if (newRs.compareTo(rsLoc) != 0) {
480 LOG.debug("Region with " + splitAlgo.rowToStr(split)
481 + " moved to " + newRs + ". Relocating...");
482
483 if (!daughterRegions.containsKey(newRs)) {
484 LinkedList<Pair<byte[], byte[]>> entry = Lists.newLinkedList();
485 daughterRegions.put(newRs, entry);
486 }
487 daughterRegions.get(newRs).add(dr);
488 dr = null;
489 continue;
490 }
491
492
493 byte[] sk = regionLoc.getRegionInfo().getStartKey();
494 if (sk.length != 0) {
495 if (Bytes.equals(split, sk)) {
496 LOG.debug("Region already split on "
497 + splitAlgo.rowToStr(split) + ". Skipping this region...");
498 ++splitCount;
499 dr = null;
500 continue;
501 }
502 byte[] start = dr.getFirst();
503 Preconditions.checkArgument(Bytes.equals(start, sk), splitAlgo
504 .rowToStr(start) + " != " + splitAlgo.rowToStr(sk));
505 }
506
507
508 break;
509 }
510 if (regionList.isEmpty()) {
511 daughterRegions.remove(rsLoc);
512 }
513 if (dr == null)
514 continue;
515
516
517 byte[] split = dr.getSecond();
518 LOG.debug("Splitting at " + splitAlgo.rowToStr(split));
519 HBaseAdmin admin = new HBaseAdmin(table.getConfiguration());
520 admin.split(table.getTableName(), split);
521
522 LinkedList<Pair<byte[], byte[]>> finished = Lists.newLinkedList();
523 if (conf.getBoolean("split.verify", true)) {
524
525 outstanding.addLast(dr);
526
527 while (outstanding.size() >= MAX_OUTSTANDING) {
528 finished = splitScan(outstanding, table, splitAlgo);
529 if (finished.isEmpty()) {
530 Thread.sleep(30 * 1000);
531 } else {
532 outstanding.removeAll(finished);
533 }
534 }
535 } else {
536 finished.add(dr);
537 }
538
539
540 for (Pair<byte[], byte[]> region : finished) {
541 splitOut.writeChars("- " + splitAlgo.rowToStr(region.getFirst())
542 + " " + splitAlgo.rowToStr(region.getSecond()) + "\n");
543 splitCount++;
544 if (splitCount % 10 == 0) {
545 long tDiff = (System.currentTimeMillis() - startTime)
546 / splitCount;
547 LOG.debug("STATUS UPDATE: " + splitCount + " / " + origCount
548 + ". Avg Time / Split = "
549 + org.apache.hadoop.util.StringUtils.formatTime(tDiff));
550 }
551 }
552 }
553 }
554 if (conf.getBoolean("split.verify", true)) {
555 while (!outstanding.isEmpty()) {
556 LinkedList<Pair<byte[], byte[]>> finished = splitScan(outstanding,
557 table, splitAlgo);
558 if (finished.isEmpty()) {
559 Thread.sleep(30 * 1000);
560 } else {
561 outstanding.removeAll(finished);
562 for (Pair<byte[], byte[]> region : finished) {
563 splitOut.writeChars("- " + splitAlgo.rowToStr(region.getFirst())
564 + " " + splitAlgo.rowToStr(region.getSecond()) + "\n");
565 }
566 }
567 }
568 }
569 LOG.debug("All regions have been successfully split!");
570 } finally {
571 long tDiff = System.currentTimeMillis() - startTime;
572 LOG.debug("TOTAL TIME = "
573 + org.apache.hadoop.util.StringUtils.formatTime(tDiff));
574 LOG.debug("Splits = " + splitCount);
575 LOG.debug("Avg Time / Split = "
576 + org.apache.hadoop.util.StringUtils.formatTime(tDiff / splitCount));
577
578 splitOut.close();
579 if (table != null){
580 table.close();
581 }
582 }
583 fs.delete(splitFile, false);
584 }
585
586
587
588
589
590 public static SplitAlgorithm newSplitAlgoInstance(Configuration conf,
591 String splitClassName) throws IOException {
592 Class<?> splitClass;
593
594
595
596 if(splitClassName.equals(HexStringSplit.class.getSimpleName())) {
597 splitClass = HexStringSplit.class;
598 } else if (splitClassName.equals(UniformSplit.class.getSimpleName())) {
599 splitClass = UniformSplit.class;
600 } else {
601 try {
602 splitClass = conf.getClassByName(splitClassName);
603 } catch (ClassNotFoundException e) {
604 throw new IOException("Couldn't load split class " + splitClassName, e);
605 }
606 if(splitClass == null) {
607 throw new IOException("Failed loading split class " + splitClassName);
608 }
609 if(!SplitAlgorithm.class.isAssignableFrom(splitClass)) {
610 throw new IOException(
611 "Specified split class doesn't implement SplitAlgorithm");
612 }
613 }
614 try {
615 return splitClass.asSubclass(SplitAlgorithm.class).newInstance();
616 } catch (Exception e) {
617 throw new IOException("Problem loading split algorithm: ", e);
618 }
619 }
620
621 static LinkedList<Pair<byte[], byte[]>> splitScan(
622 LinkedList<Pair<byte[], byte[]>> regionList, HTable table,
623 SplitAlgorithm splitAlgo)
624 throws IOException, InterruptedException {
625 LinkedList<Pair<byte[], byte[]>> finished = Lists.newLinkedList();
626 LinkedList<Pair<byte[], byte[]>> logicalSplitting = Lists.newLinkedList();
627 LinkedList<Pair<byte[], byte[]>> physicalSplitting = Lists.newLinkedList();
628
629
630 Path rootDir = FSUtils.getRootDir(table.getConfiguration());
631 Path tableDir = HTableDescriptor.getTableDir(rootDir, table.getTableName());
632 FileSystem fs = tableDir.getFileSystem(table.getConfiguration());
633 HTableDescriptor htd = table.getTableDescriptor();
634
635
636 table.clearRegionCache();
637
638
639 for (Pair<byte[], byte[]> region : regionList) {
640 byte[] start = region.getFirst();
641 byte[] split = region.getSecond();
642
643
644 try {
645 HRegionInfo dri = table.getRegionLocation(split).getRegionInfo();
646 if (dri.isOffline() || !Bytes.equals(dri.getStartKey(), split)) {
647 logicalSplitting.add(region);
648 continue;
649 }
650 } catch (NoServerForRegionException nsfre) {
651
652 LOG.info(nsfre);
653 logicalSplitting.add(region);
654 continue;
655 }
656
657 try {
658
659
660 LinkedList<HRegionInfo> check = Lists.newLinkedList();
661 check.add(table.getRegionLocation(start).getRegionInfo());
662 check.add(table.getRegionLocation(split).getRegionInfo());
663 for (HRegionInfo hri : check.toArray(new HRegionInfo[] {})) {
664 byte[] sk = hri.getStartKey();
665 if (sk.length == 0)
666 sk = splitAlgo.firstRow();
667 String startKey = splitAlgo.rowToStr(sk);
668
669 HRegionFileSystem regionFs = HRegionFileSystem.openRegionFromFileSystem(
670 table.getConfiguration(), fs, tableDir, hri, true);
671
672
673 boolean refFound = false;
674 for (HColumnDescriptor c : htd.getFamilies()) {
675 if ((refFound = regionFs.hasReferences(htd.getNameAsString()))) {
676 break;
677 }
678 }
679
680
681 if (!refFound) {
682 check.remove(hri);
683 }
684 }
685 if (check.isEmpty()) {
686 finished.add(region);
687 } else {
688 physicalSplitting.add(region);
689 }
690 } catch (NoServerForRegionException nsfre) {
691 LOG.debug("No Server Exception thrown for: " + splitAlgo.rowToStr(start));
692 physicalSplitting.add(region);
693 table.clearRegionCache();
694 }
695 }
696
697 LOG.debug("Split Scan: " + finished.size() + " finished / "
698 + logicalSplitting.size() + " split wait / "
699 + physicalSplitting.size() + " reference wait");
700
701 return finished;
702 }
703
704 static LinkedList<Pair<byte[], byte[]>> getSplits(HTable table,
705 SplitAlgorithm splitAlgo) throws IOException {
706 Path hbDir = FSUtils.getRootDir(table.getConfiguration());
707 Path tableDir = HTableDescriptor.getTableDir(hbDir, table.getTableName());
708 Path splitFile = new Path(tableDir, "_balancedSplit");
709 FileSystem fs = tableDir.getFileSystem(table.getConfiguration());
710
711
712 Set<Pair<String, String>> daughterRegions = Sets.newHashSet();
713
714
715 if (!fs.exists(splitFile)) {
716
717 LOG.debug("No _balancedSplit file. Calculating splits...");
718
719
720 Set<Pair<byte[], byte[]>> rows = Sets.newHashSet();
721 Pair<byte[][], byte[][]> tmp = table.getStartEndKeys();
722 Preconditions.checkArgument(
723 tmp.getFirst().length == tmp.getSecond().length,
724 "Start and End rows should be equivalent");
725 for (int i = 0; i < tmp.getFirst().length; ++i) {
726 byte[] start = tmp.getFirst()[i], end = tmp.getSecond()[i];
727 if (start.length == 0)
728 start = splitAlgo.firstRow();
729 if (end.length == 0)
730 end = splitAlgo.lastRow();
731 rows.add(Pair.newPair(start, end));
732 }
733 LOG.debug("Table " + Bytes.toString(table.getTableName()) + " has "
734 + rows.size() + " regions that will be split.");
735
736
737 Path tmpFile = new Path(tableDir, "_balancedSplit_prepare");
738 FSDataOutputStream tmpOut = fs.create(tmpFile);
739
740
741 for (Pair<byte[], byte[]> r : rows) {
742 byte[] splitPoint = splitAlgo.split(r.getFirst(), r.getSecond());
743 String startStr = splitAlgo.rowToStr(r.getFirst());
744 String splitStr = splitAlgo.rowToStr(splitPoint);
745 daughterRegions.add(Pair.newPair(startStr, splitStr));
746 LOG.debug("Will Split [" + startStr + " , "
747 + splitAlgo.rowToStr(r.getSecond()) + ") at " + splitStr);
748 tmpOut.writeChars("+ " + startStr + splitAlgo.separator() + splitStr
749 + "\n");
750 }
751 tmpOut.close();
752 fs.rename(tmpFile, splitFile);
753 } else {
754 LOG.debug("_balancedSplit file found. Replay log to restore state...");
755 FSUtils.getInstance(fs, table.getConfiguration())
756 .recoverFileLease(fs, splitFile, table.getConfiguration());
757
758
759 FSDataInputStream tmpIn = fs.open(splitFile);
760 StringBuilder sb = new StringBuilder(tmpIn.available());
761 while (tmpIn.available() > 0) {
762 sb.append(tmpIn.readChar());
763 }
764 tmpIn.close();
765 for (String line : sb.toString().split("\n")) {
766 String[] cmd = line.split(splitAlgo.separator());
767 Preconditions.checkArgument(3 == cmd.length);
768 byte[] start = splitAlgo.strToRow(cmd[1]);
769 String startStr = splitAlgo.rowToStr(start);
770 byte[] splitPoint = splitAlgo.strToRow(cmd[2]);
771 String splitStr = splitAlgo.rowToStr(splitPoint);
772 Pair<String, String> r = Pair.newPair(startStr, splitStr);
773 if (cmd[0].equals("+")) {
774 LOG.debug("Adding: " + r);
775 daughterRegions.add(r);
776 } else {
777 LOG.debug("Removing: " + r);
778 Preconditions.checkArgument(cmd[0].equals("-"),
779 "Unknown option: " + cmd[0]);
780 Preconditions.checkState(daughterRegions.contains(r),
781 "Missing row: " + r);
782 daughterRegions.remove(r);
783 }
784 }
785 LOG.debug("Done reading. " + daughterRegions.size() + " regions left.");
786 }
787 LinkedList<Pair<byte[], byte[]>> ret = Lists.newLinkedList();
788 for (Pair<String, String> r : daughterRegions) {
789 ret.add(Pair.newPair(splitAlgo.strToRow(r.getFirst()), splitAlgo
790 .strToRow(r.getSecond())));
791 }
792 return ret;
793 }
794
795
796
797
798
799
800
801
802
803
804
805
806 public static class HexStringSplit implements SplitAlgorithm {
807 final static String DEFAULT_MIN_HEX = "00000000";
808 final static String DEFAULT_MAX_HEX = "FFFFFFFF";
809
810 String firstRow = DEFAULT_MIN_HEX;
811 BigInteger firstRowInt = BigInteger.ZERO;
812 String lastRow = DEFAULT_MAX_HEX;
813 BigInteger lastRowInt = new BigInteger(lastRow, 16);
814 int rowComparisonLength = lastRow.length();
815
816 public byte[] split(byte[] start, byte[] end) {
817 BigInteger s = convertToBigInteger(start);
818 BigInteger e = convertToBigInteger(end);
819 Preconditions.checkArgument(!e.equals(BigInteger.ZERO));
820 return convertToByte(split2(s, e));
821 }
822
823 public byte[][] split(int n) {
824 Preconditions.checkArgument(lastRowInt.compareTo(firstRowInt) > 0,
825 "last row (%s) is configured less than first row (%s)", lastRow,
826 firstRow);
827
828 BigInteger range = lastRowInt.subtract(firstRowInt).add(BigInteger.ONE);
829 Preconditions.checkState(range.compareTo(BigInteger.valueOf(n)) >= 0,
830 "split granularity (%s) is greater than the range (%s)", n, range);
831
832 BigInteger[] splits = new BigInteger[n - 1];
833 BigInteger sizeOfEachSplit = range.divide(BigInteger.valueOf(n));
834 for (int i = 1; i < n; i++) {
835
836
837 splits[i - 1] = firstRowInt.add(sizeOfEachSplit.multiply(BigInteger
838 .valueOf(i)));
839 }
840 return convertToBytes(splits);
841 }
842
843 public byte[] firstRow() {
844 return convertToByte(firstRowInt);
845 }
846
847 public byte[] lastRow() {
848 return convertToByte(lastRowInt);
849 }
850
851 public void setFirstRow(String userInput) {
852 firstRow = userInput;
853 firstRowInt = new BigInteger(firstRow, 16);
854 }
855
856 public void setLastRow(String userInput) {
857 lastRow = userInput;
858 lastRowInt = new BigInteger(lastRow, 16);
859
860 rowComparisonLength = lastRow.length();
861 }
862
863 public byte[] strToRow(String in) {
864 return convertToByte(new BigInteger(in, 16));
865 }
866
867 public String rowToStr(byte[] row) {
868 return Bytes.toStringBinary(row);
869 }
870
871 public String separator() {
872 return " ";
873 }
874
875
876
877
878
879
880
881
882 public BigInteger split2(BigInteger a, BigInteger b) {
883 return a.add(b).divide(BigInteger.valueOf(2)).abs();
884 }
885
886
887
888
889
890
891
892 public byte[][] convertToBytes(BigInteger[] bigIntegers) {
893 byte[][] returnBytes = new byte[bigIntegers.length][];
894 for (int i = 0; i < bigIntegers.length; i++) {
895 returnBytes[i] = convertToByte(bigIntegers[i]);
896 }
897 return returnBytes;
898 }
899
900
901
902
903
904
905
906
907 public static byte[] convertToByte(BigInteger bigInteger, int pad) {
908 String bigIntegerString = bigInteger.toString(16);
909 bigIntegerString = StringUtils.leftPad(bigIntegerString, pad, '0');
910 return Bytes.toBytes(bigIntegerString);
911 }
912
913
914
915
916
917
918
919 public byte[] convertToByte(BigInteger bigInteger) {
920 return convertToByte(bigInteger, rowComparisonLength);
921 }
922
923
924
925
926
927
928
929 public BigInteger convertToBigInteger(byte[] row) {
930 return (row.length > 0) ? new BigInteger(Bytes.toString(row), 16)
931 : BigInteger.ZERO;
932 }
933
934 @Override
935 public String toString() {
936 return this.getClass().getSimpleName() + " [" + rowToStr(firstRow())
937 + "," + rowToStr(lastRow()) + "]";
938 }
939 }
940
941
942
943
944
945
946
947
948
949 public static class UniformSplit implements SplitAlgorithm {
950 static final byte xFF = (byte) 0xFF;
951 byte[] firstRowBytes = ArrayUtils.EMPTY_BYTE_ARRAY;
952 byte[] lastRowBytes =
953 new byte[] {xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF};
954 public byte[] split(byte[] start, byte[] end) {
955 return Bytes.split(start, end, 1)[1];
956 }
957
958 @Override
959 public byte[][] split(int numRegions) {
960 Preconditions.checkArgument(
961 Bytes.compareTo(lastRowBytes, firstRowBytes) > 0,
962 "last row (%s) is configured less than first row (%s)",
963 Bytes.toStringBinary(lastRowBytes),
964 Bytes.toStringBinary(firstRowBytes));
965
966 byte[][] splits = Bytes.split(firstRowBytes, lastRowBytes, true,
967 numRegions - 1);
968 Preconditions.checkState(splits != null,
969 "Could not split region with given user input: " + this);
970
971
972 return Arrays.copyOfRange(splits, 1, splits.length - 1);
973 }
974
975 @Override
976 public byte[] firstRow() {
977 return firstRowBytes;
978 }
979
980 @Override
981 public byte[] lastRow() {
982 return lastRowBytes;
983 }
984
985 @Override
986 public void setFirstRow(String userInput) {
987 firstRowBytes = Bytes.toBytesBinary(userInput);
988 }
989
990 @Override
991 public void setLastRow(String userInput) {
992 lastRowBytes = Bytes.toBytesBinary(userInput);
993 }
994
995 @Override
996 public byte[] strToRow(String input) {
997 return Bytes.toBytesBinary(input);
998 }
999
1000 @Override
1001 public String rowToStr(byte[] row) {
1002 return Bytes.toStringBinary(row);
1003 }
1004
1005 @Override
1006 public String separator() {
1007 return ",";
1008 }
1009
1010 @Override
1011 public String toString() {
1012 return this.getClass().getSimpleName() + " [" + rowToStr(firstRow())
1013 + "," + rowToStr(lastRow()) + "]";
1014 }
1015 }
1016 }