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