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.hbase.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 LinkedList<Pair<byte[], byte[]>> local_finished = Lists.newLinkedList();
536 if (conf.getBoolean("split.verify", true)) {
537
538 outstanding.addLast(dr);
539
540 while (outstanding.size() >= MAX_OUTSTANDING) {
541 LOG.debug("Wait for outstanding splits " + outstanding.size());
542 local_finished = splitScan(outstanding, table, splitAlgo);
543 if (local_finished.isEmpty()) {
544 Thread.sleep(30 * 1000);
545 } else {
546 finished.addAll(local_finished);
547 outstanding.removeAll(local_finished);
548 LOG.debug(local_finished.size() + " outstanding splits finished");
549 }
550 }
551 } else {
552 finished.add(dr);
553 }
554
555
556 for (Pair<byte[], byte[]> region : finished) {
557 splitOut.writeChars("- " + splitAlgo.rowToStr(region.getFirst())
558 + " " + splitAlgo.rowToStr(region.getSecond()) + "\n");
559 splitCount++;
560 if (splitCount % 10 == 0) {
561 long tDiff = (System.currentTimeMillis() - startTime)
562 / splitCount;
563 LOG.debug("STATUS UPDATE: " + splitCount + " / " + origCount
564 + ". Avg Time / Split = "
565 + org.apache.hadoop.util.StringUtils.formatTime(tDiff));
566 }
567 }
568 }
569 }
570 if (conf.getBoolean("split.verify", true)) {
571 while (!outstanding.isEmpty()) {
572 LOG.debug("Finally Wait for outstanding splits " + outstanding.size());
573 LinkedList<Pair<byte[], byte[]>> finished = splitScan(outstanding,
574 table, splitAlgo);
575 if (finished.isEmpty()) {
576 Thread.sleep(30 * 1000);
577 } else {
578 outstanding.removeAll(finished);
579 for (Pair<byte[], byte[]> region : finished) {
580 splitOut.writeChars("- " + splitAlgo.rowToStr(region.getFirst())
581 + " " + splitAlgo.rowToStr(region.getSecond()) + "\n");
582 splitCount++;
583 }
584 LOG.debug("Finally " + finished.size() + " outstanding splits finished");
585 }
586 }
587 }
588 LOG.debug("All regions have been successfully split!");
589 } finally {
590 long tDiff = System.currentTimeMillis() - startTime;
591 LOG.debug("TOTAL TIME = "
592 + org.apache.hadoop.util.StringUtils.formatTime(tDiff));
593 LOG.debug("Splits = " + splitCount);
594 if (0 < splitCount) {
595 LOG.debug("Avg Time / Split = "
596 + org.apache.hadoop.util.StringUtils.formatTime(tDiff / splitCount));
597 }
598
599 splitOut.close();
600 if (table != null){
601 table.close();
602 }
603 }
604 fs.delete(splitFile, false);
605 }
606
607
608
609
610
611 public static SplitAlgorithm newSplitAlgoInstance(Configuration conf,
612 String splitClassName) throws IOException {
613 Class<?> splitClass;
614
615
616
617 if(splitClassName.equals(HexStringSplit.class.getSimpleName())) {
618 splitClass = HexStringSplit.class;
619 } else if (splitClassName.equals(UniformSplit.class.getSimpleName())) {
620 splitClass = UniformSplit.class;
621 } else {
622 try {
623 splitClass = conf.getClassByName(splitClassName);
624 } catch (ClassNotFoundException e) {
625 throw new IOException("Couldn't load split class " + splitClassName, e);
626 }
627 if(splitClass == null) {
628 throw new IOException("Failed loading split class " + splitClassName);
629 }
630 if(!SplitAlgorithm.class.isAssignableFrom(splitClass)) {
631 throw new IOException(
632 "Specified split class doesn't implement SplitAlgorithm");
633 }
634 }
635 try {
636 return splitClass.asSubclass(SplitAlgorithm.class).newInstance();
637 } catch (Exception e) {
638 throw new IOException("Problem loading split algorithm: ", e);
639 }
640 }
641
642 static LinkedList<Pair<byte[], byte[]>> splitScan(
643 LinkedList<Pair<byte[], byte[]>> regionList, HTable table,
644 SplitAlgorithm splitAlgo)
645 throws IOException, InterruptedException {
646 LinkedList<Pair<byte[], byte[]>> finished = Lists.newLinkedList();
647 LinkedList<Pair<byte[], byte[]>> logicalSplitting = Lists.newLinkedList();
648 LinkedList<Pair<byte[], byte[]>> physicalSplitting = Lists.newLinkedList();
649
650
651 Path rootDir = FSUtils.getRootDir(table.getConfiguration());
652 Path tableDir = FSUtils.getTableDir(rootDir, table.getName());
653 FileSystem fs = tableDir.getFileSystem(table.getConfiguration());
654 HTableDescriptor htd = table.getTableDescriptor();
655
656
657 table.clearRegionCache();
658
659
660 for (Pair<byte[], byte[]> region : regionList) {
661 byte[] start = region.getFirst();
662 byte[] split = region.getSecond();
663
664
665 try {
666 HRegionInfo dri = table.getRegionLocation(split).getRegionInfo();
667 if (dri.isOffline() || !Bytes.equals(dri.getStartKey(), split)) {
668 logicalSplitting.add(region);
669 continue;
670 }
671 } catch (NoServerForRegionException nsfre) {
672
673 LOG.info(nsfre);
674 logicalSplitting.add(region);
675 continue;
676 }
677
678 try {
679
680
681 LinkedList<HRegionInfo> check = Lists.newLinkedList();
682 check.add(table.getRegionLocation(start).getRegionInfo());
683 check.add(table.getRegionLocation(split).getRegionInfo());
684 for (HRegionInfo hri : check.toArray(new HRegionInfo[check.size()])) {
685 byte[] sk = hri.getStartKey();
686 if (sk.length == 0)
687 sk = splitAlgo.firstRow();
688 String startKey = splitAlgo.rowToStr(sk);
689
690 HRegionFileSystem regionFs = HRegionFileSystem.openRegionFromFileSystem(
691 table.getConfiguration(), fs, tableDir, hri, true);
692
693
694 boolean refFound = false;
695 for (HColumnDescriptor c : htd.getFamilies()) {
696 if ((refFound = regionFs.hasReferences(htd.getTableName().getNameAsString()))) {
697 break;
698 }
699 }
700
701
702 if (!refFound) {
703 check.remove(hri);
704 }
705 }
706 if (check.isEmpty()) {
707 finished.add(region);
708 } else {
709 physicalSplitting.add(region);
710 }
711 } catch (NoServerForRegionException nsfre) {
712 LOG.debug("No Server Exception thrown for: " + splitAlgo.rowToStr(start));
713 physicalSplitting.add(region);
714 table.clearRegionCache();
715 }
716 }
717
718 LOG.debug("Split Scan: " + finished.size() + " finished / "
719 + logicalSplitting.size() + " split wait / "
720 + physicalSplitting.size() + " reference wait");
721
722 return finished;
723 }
724
725 static LinkedList<Pair<byte[], byte[]>> getSplits(HTable table,
726 SplitAlgorithm splitAlgo) throws IOException {
727 Path hbDir = FSUtils.getRootDir(table.getConfiguration());
728 Path tableDir = FSUtils.getTableDir(hbDir, table.getName());
729 Path splitFile = new Path(tableDir, "_balancedSplit");
730 FileSystem fs = tableDir.getFileSystem(table.getConfiguration());
731
732
733 Set<Pair<String, String>> daughterRegions = Sets.newHashSet();
734
735
736 if (!fs.exists(splitFile)) {
737
738 LOG.debug("No _balancedSplit file. Calculating splits...");
739
740
741 Set<Pair<byte[], byte[]>> rows = Sets.newHashSet();
742 Pair<byte[][], byte[][]> tmp = table.getStartEndKeys();
743 Preconditions.checkArgument(
744 tmp.getFirst().length == tmp.getSecond().length,
745 "Start and End rows should be equivalent");
746 for (int i = 0; i < tmp.getFirst().length; ++i) {
747 byte[] start = tmp.getFirst()[i], end = tmp.getSecond()[i];
748 if (start.length == 0)
749 start = splitAlgo.firstRow();
750 if (end.length == 0)
751 end = splitAlgo.lastRow();
752 rows.add(Pair.newPair(start, end));
753 }
754 LOG.debug("Table " + Bytes.toString(table.getTableName()) + " has "
755 + rows.size() + " regions that will be split.");
756
757
758 Path tmpFile = new Path(tableDir, "_balancedSplit_prepare");
759 FSDataOutputStream tmpOut = fs.create(tmpFile);
760
761
762 for (Pair<byte[], byte[]> r : rows) {
763 byte[] splitPoint = splitAlgo.split(r.getFirst(), r.getSecond());
764 String startStr = splitAlgo.rowToStr(r.getFirst());
765 String splitStr = splitAlgo.rowToStr(splitPoint);
766 daughterRegions.add(Pair.newPair(startStr, splitStr));
767 LOG.debug("Will Split [" + startStr + " , "
768 + splitAlgo.rowToStr(r.getSecond()) + ") at " + splitStr);
769 tmpOut.writeChars("+ " + startStr + splitAlgo.separator() + splitStr
770 + "\n");
771 }
772 tmpOut.close();
773 fs.rename(tmpFile, splitFile);
774 } else {
775 LOG.debug("_balancedSplit file found. Replay log to restore state...");
776 FSUtils.getInstance(fs, table.getConfiguration())
777 .recoverFileLease(fs, splitFile, table.getConfiguration(), null);
778
779
780 FSDataInputStream tmpIn = fs.open(splitFile);
781 StringBuilder sb = new StringBuilder(tmpIn.available());
782 while (tmpIn.available() > 0) {
783 sb.append(tmpIn.readChar());
784 }
785 tmpIn.close();
786 for (String line : sb.toString().split("\n")) {
787 String[] cmd = line.split(splitAlgo.separator());
788 Preconditions.checkArgument(3 == cmd.length);
789 byte[] start = splitAlgo.strToRow(cmd[1]);
790 String startStr = splitAlgo.rowToStr(start);
791 byte[] splitPoint = splitAlgo.strToRow(cmd[2]);
792 String splitStr = splitAlgo.rowToStr(splitPoint);
793 Pair<String, String> r = Pair.newPair(startStr, splitStr);
794 if (cmd[0].equals("+")) {
795 LOG.debug("Adding: " + r);
796 daughterRegions.add(r);
797 } else {
798 LOG.debug("Removing: " + r);
799 Preconditions.checkArgument(cmd[0].equals("-"),
800 "Unknown option: " + cmd[0]);
801 Preconditions.checkState(daughterRegions.contains(r),
802 "Missing row: " + r);
803 daughterRegions.remove(r);
804 }
805 }
806 LOG.debug("Done reading. " + daughterRegions.size() + " regions left.");
807 }
808 LinkedList<Pair<byte[], byte[]>> ret = Lists.newLinkedList();
809 for (Pair<String, String> r : daughterRegions) {
810 ret.add(Pair.newPair(splitAlgo.strToRow(r.getFirst()), splitAlgo
811 .strToRow(r.getSecond())));
812 }
813 return ret;
814 }
815
816
817
818
819
820
821
822
823
824
825
826
827 public static class HexStringSplit implements SplitAlgorithm {
828 final static String DEFAULT_MIN_HEX = "00000000";
829 final static String DEFAULT_MAX_HEX = "FFFFFFFF";
830
831 String firstRow = DEFAULT_MIN_HEX;
832 BigInteger firstRowInt = BigInteger.ZERO;
833 String lastRow = DEFAULT_MAX_HEX;
834 BigInteger lastRowInt = new BigInteger(lastRow, 16);
835 int rowComparisonLength = lastRow.length();
836
837 public byte[] split(byte[] start, byte[] end) {
838 BigInteger s = convertToBigInteger(start);
839 BigInteger e = convertToBigInteger(end);
840 Preconditions.checkArgument(!e.equals(BigInteger.ZERO));
841 return convertToByte(split2(s, e));
842 }
843
844 public byte[][] split(int n) {
845 Preconditions.checkArgument(lastRowInt.compareTo(firstRowInt) > 0,
846 "last row (%s) is configured less than first row (%s)", lastRow,
847 firstRow);
848
849 BigInteger range = lastRowInt.subtract(firstRowInt).add(BigInteger.ONE);
850 Preconditions.checkState(range.compareTo(BigInteger.valueOf(n)) >= 0,
851 "split granularity (%s) is greater than the range (%s)", n, range);
852
853 BigInteger[] splits = new BigInteger[n - 1];
854 BigInteger sizeOfEachSplit = range.divide(BigInteger.valueOf(n));
855 for (int i = 1; i < n; i++) {
856
857
858 splits[i - 1] = firstRowInt.add(sizeOfEachSplit.multiply(BigInteger
859 .valueOf(i)));
860 }
861 return convertToBytes(splits);
862 }
863
864 public byte[] firstRow() {
865 return convertToByte(firstRowInt);
866 }
867
868 public byte[] lastRow() {
869 return convertToByte(lastRowInt);
870 }
871
872 public void setFirstRow(String userInput) {
873 firstRow = userInput;
874 firstRowInt = new BigInteger(firstRow, 16);
875 }
876
877 public void setLastRow(String userInput) {
878 lastRow = userInput;
879 lastRowInt = new BigInteger(lastRow, 16);
880
881 rowComparisonLength = lastRow.length();
882 }
883
884 public byte[] strToRow(String in) {
885 return convertToByte(new BigInteger(in, 16));
886 }
887
888 public String rowToStr(byte[] row) {
889 return Bytes.toStringBinary(row);
890 }
891
892 public String separator() {
893 return " ";
894 }
895
896 @Override
897 public void setFirstRow(byte[] userInput) {
898 firstRow = Bytes.toString(userInput);
899 }
900
901 @Override
902 public void setLastRow(byte[] userInput) {
903 lastRow = Bytes.toString(userInput);
904 }
905
906
907
908
909
910
911
912
913 public BigInteger split2(BigInteger a, BigInteger b) {
914 return a.add(b).divide(BigInteger.valueOf(2)).abs();
915 }
916
917
918
919
920
921
922
923 public byte[][] convertToBytes(BigInteger[] bigIntegers) {
924 byte[][] returnBytes = new byte[bigIntegers.length][];
925 for (int i = 0; i < bigIntegers.length; i++) {
926 returnBytes[i] = convertToByte(bigIntegers[i]);
927 }
928 return returnBytes;
929 }
930
931
932
933
934
935
936
937
938 public static byte[] convertToByte(BigInteger bigInteger, int pad) {
939 String bigIntegerString = bigInteger.toString(16);
940 bigIntegerString = StringUtils.leftPad(bigIntegerString, pad, '0');
941 return Bytes.toBytes(bigIntegerString);
942 }
943
944
945
946
947
948
949
950 public byte[] convertToByte(BigInteger bigInteger) {
951 return convertToByte(bigInteger, rowComparisonLength);
952 }
953
954
955
956
957
958
959
960 public BigInteger convertToBigInteger(byte[] row) {
961 return (row.length > 0) ? new BigInteger(Bytes.toString(row), 16)
962 : BigInteger.ZERO;
963 }
964
965 @Override
966 public String toString() {
967 return this.getClass().getSimpleName() + " [" + rowToStr(firstRow())
968 + "," + rowToStr(lastRow()) + "]";
969 }
970 }
971
972
973
974
975
976
977
978
979
980 public static class UniformSplit implements SplitAlgorithm {
981 static final byte xFF = (byte) 0xFF;
982 byte[] firstRowBytes = ArrayUtils.EMPTY_BYTE_ARRAY;
983 byte[] lastRowBytes =
984 new byte[] {xFF, xFF, xFF, xFF, xFF, xFF, xFF, xFF};
985 public byte[] split(byte[] start, byte[] end) {
986 return Bytes.split(start, end, 1)[1];
987 }
988
989 @Override
990 public byte[][] split(int numRegions) {
991 Preconditions.checkArgument(
992 Bytes.compareTo(lastRowBytes, firstRowBytes) > 0,
993 "last row (%s) is configured less than first row (%s)",
994 Bytes.toStringBinary(lastRowBytes),
995 Bytes.toStringBinary(firstRowBytes));
996
997 byte[][] splits = Bytes.split(firstRowBytes, lastRowBytes, true,
998 numRegions - 1);
999 Preconditions.checkState(splits != null,
1000 "Could not split region with given user input: " + this);
1001
1002
1003 return Arrays.copyOfRange(splits, 1, splits.length - 1);
1004 }
1005
1006 @Override
1007 public byte[] firstRow() {
1008 return firstRowBytes;
1009 }
1010
1011 @Override
1012 public byte[] lastRow() {
1013 return lastRowBytes;
1014 }
1015
1016 @Override
1017 public void setFirstRow(String userInput) {
1018 firstRowBytes = Bytes.toBytesBinary(userInput);
1019 }
1020
1021 @Override
1022 public void setLastRow(String userInput) {
1023 lastRowBytes = Bytes.toBytesBinary(userInput);
1024 }
1025
1026
1027 @Override
1028 public void setFirstRow(byte[] userInput) {
1029 firstRowBytes = userInput;
1030 }
1031
1032 @Override
1033 public void setLastRow(byte[] userInput) {
1034 lastRowBytes = userInput;
1035 }
1036
1037 @Override
1038 public byte[] strToRow(String input) {
1039 return Bytes.toBytesBinary(input);
1040 }
1041
1042 @Override
1043 public String rowToStr(byte[] row) {
1044 return Bytes.toStringBinary(row);
1045 }
1046
1047 @Override
1048 public String separator() {
1049 return ",";
1050 }
1051
1052 @Override
1053 public String toString() {
1054 return this.getClass().getSimpleName() + " [" + rowToStr(firstRow())
1055 + "," + rowToStr(lastRow()) + "]";
1056 }
1057 }
1058 }