1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase;
21
22 import java.io.DataInput;
23 import java.io.DataOutput;
24 import java.io.EOFException;
25 import java.io.IOException;
26 import java.util.Arrays;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.fs.FileSystem;
32 import org.apache.hadoop.fs.Path;
33 import org.apache.hadoop.hbase.KeyValue.KVComparator;
34 import org.apache.hadoop.hbase.migration.HRegionInfo090x;
35 import org.apache.hadoop.hbase.util.Bytes;
36 import org.apache.hadoop.hbase.util.FSTableDescriptors;
37 import org.apache.hadoop.hbase.util.JenkinsHash;
38 import org.apache.hadoop.hbase.util.MD5Hash;
39 import org.apache.hadoop.io.VersionedWritable;
40 import org.apache.hadoop.io.WritableComparable;
41
42
43
44
45
46
47 public class HRegionInfo extends VersionedWritable
48 implements WritableComparable<HRegionInfo> {
49
50 public static final byte VERSION_PRE_092 = 0;
51 public static final byte VERSION = 1;
52 private static final Log LOG = LogFactory.getLog(HRegionInfo.class);
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 private static final int ENC_SEPARATOR = '.';
81 public static final int MD5_HEX_LENGTH = 32;
82
83
84 public static final String ENCODED_REGION_NAME_REGEX = "(?:[a-f0-9]+)";
85
86
87
88
89
90
91
92 private static boolean hasEncodedName(final byte[] regionName) {
93
94 if ((regionName.length >= 1)
95 && (regionName[regionName.length - 1] == ENC_SEPARATOR)) {
96
97 return true;
98 }
99 return false;
100 }
101
102
103
104
105
106 public static String encodeRegionName(final byte [] regionName) {
107 String encodedName;
108 if (hasEncodedName(regionName)) {
109
110
111 encodedName = Bytes.toString(regionName,
112 regionName.length - MD5_HEX_LENGTH - 1,
113 MD5_HEX_LENGTH);
114 } else {
115
116
117 int hashVal = Math.abs(JenkinsHash.getInstance().hash(regionName,
118 regionName.length, 0));
119 encodedName = String.valueOf(hashVal);
120 }
121 return encodedName;
122 }
123
124
125
126
127
128
129
130
131 public static String prettyPrint(final String encodedRegionName) {
132 if (encodedRegionName.equals("70236052")) {
133 return encodedRegionName + "/-ROOT-";
134 } else if (encodedRegionName.equals("1028785192")) {
135 return encodedRegionName + "/.META.";
136 }
137 return encodedRegionName;
138 }
139
140
141 public static final int DELIMITER = ',';
142
143
144 public static final HRegionInfo ROOT_REGIONINFO =
145 new HRegionInfo(0L, Bytes.toBytes("-ROOT-"));
146
147
148 public static final HRegionInfo FIRST_META_REGIONINFO =
149 new HRegionInfo(1L, Bytes.toBytes(".META."));
150
151 private byte [] endKey = HConstants.EMPTY_BYTE_ARRAY;
152
153
154
155 private boolean offLine = false;
156 private long regionId = -1;
157 private transient byte [] regionName = HConstants.EMPTY_BYTE_ARRAY;
158 private String regionNameStr = "";
159 private boolean split = false;
160 private byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
161 private int hashCode = -1;
162
163 public static final String NO_HASH = null;
164 private volatile String encodedName = NO_HASH;
165 private byte [] encodedNameAsBytes = null;
166
167
168 private byte[] tableName = null;
169
170 private void setHashCode() {
171 int result = Arrays.hashCode(this.regionName);
172 result ^= this.regionId;
173 result ^= Arrays.hashCode(this.startKey);
174 result ^= Arrays.hashCode(this.endKey);
175 result ^= Boolean.valueOf(this.offLine).hashCode();
176 result ^= Arrays.hashCode(this.tableName);
177 this.hashCode = result;
178 }
179
180
181
182
183
184
185 private HRegionInfo(long regionId, byte[] tableName) {
186 super();
187 this.regionId = regionId;
188 this.tableName = tableName.clone();
189
190 this.regionName = createRegionName(tableName, null,
191 regionId, false);
192 this.regionNameStr = Bytes.toStringBinary(this.regionName);
193 setHashCode();
194 }
195
196
197 public HRegionInfo() {
198 super();
199 }
200
201
202
203
204
205 public HRegionInfo(HRegionInfo090x other) {
206 super();
207 this.endKey = other.getEndKey();
208 this.offLine = other.isOffline();
209 this.regionId = other.getRegionId();
210 this.regionName = other.getRegionName();
211 this.regionNameStr = Bytes.toStringBinary(this.regionName);
212 this.split = other.isSplit();
213 this.startKey = other.getStartKey();
214 this.hashCode = other.hashCode();
215 this.encodedName = other.getEncodedName();
216 this.tableName = other.getTableDesc().getName();
217 }
218
219 public HRegionInfo(final byte[] tableName) {
220 this(tableName, null, null);
221 }
222
223
224
225
226
227
228
229
230
231 public HRegionInfo(final byte[] tableName, final byte[] startKey,
232 final byte[] endKey)
233 throws IllegalArgumentException {
234 this(tableName, startKey, endKey, false);
235 }
236
237
238
239
240
241
242
243
244
245
246
247
248 public HRegionInfo(final byte[] tableName, final byte[] startKey,
249 final byte[] endKey, final boolean split)
250 throws IllegalArgumentException {
251 this(tableName, startKey, endKey, split, System.currentTimeMillis());
252 }
253
254
255
256
257
258
259
260
261
262
263
264
265
266 public HRegionInfo(final byte[] tableName, final byte[] startKey,
267 final byte[] endKey, final boolean split, final long regionid)
268 throws IllegalArgumentException {
269
270 super();
271 if (tableName == null) {
272 throw new IllegalArgumentException("tableName cannot be null");
273 }
274 this.tableName = tableName.clone();
275 this.offLine = false;
276 this.regionId = regionid;
277
278 this.regionName = createRegionName(this.tableName, startKey, regionId, true);
279
280 this.regionNameStr = Bytes.toStringBinary(this.regionName);
281 this.split = split;
282 this.endKey = endKey == null? HConstants.EMPTY_END_ROW: endKey.clone();
283 this.startKey = startKey == null?
284 HConstants.EMPTY_START_ROW: startKey.clone();
285 this.tableName = tableName.clone();
286 setHashCode();
287 }
288
289
290
291
292
293
294 public HRegionInfo(HRegionInfo other) {
295 super();
296 this.endKey = other.getEndKey();
297 this.offLine = other.isOffline();
298 this.regionId = other.getRegionId();
299 this.regionName = other.getRegionName();
300 this.regionNameStr = Bytes.toStringBinary(this.regionName);
301 this.split = other.isSplit();
302 this.startKey = other.getStartKey();
303 this.hashCode = other.hashCode();
304 this.encodedName = other.getEncodedName();
305 this.tableName = other.tableName;
306 }
307
308
309
310
311
312
313
314
315
316
317
318 public static byte [] createRegionName(final byte [] tableName,
319 final byte [] startKey, final long regionid, boolean newFormat) {
320 return createRegionName(tableName, startKey, Long.toString(regionid), newFormat);
321 }
322
323
324
325
326
327
328
329
330
331
332 public static byte [] createRegionName(final byte [] tableName,
333 final byte [] startKey, final String id, boolean newFormat) {
334 return createRegionName(tableName, startKey, Bytes.toBytes(id), newFormat);
335 }
336
337
338
339
340
341
342
343
344
345
346 public static byte [] createRegionName(final byte [] tableName,
347 final byte [] startKey, final byte [] id, boolean newFormat) {
348 byte [] b = new byte [tableName.length + 2 + id.length +
349 (startKey == null? 0: startKey.length) +
350 (newFormat ? (MD5_HEX_LENGTH + 2) : 0)];
351
352 int offset = tableName.length;
353 System.arraycopy(tableName, 0, b, 0, offset);
354 b[offset++] = DELIMITER;
355 if (startKey != null && startKey.length > 0) {
356 System.arraycopy(startKey, 0, b, offset, startKey.length);
357 offset += startKey.length;
358 }
359 b[offset++] = DELIMITER;
360 System.arraycopy(id, 0, b, offset, id.length);
361 offset += id.length;
362
363 if (newFormat) {
364
365
366
367
368
369
370
371 String md5Hash = MD5Hash.getMD5AsHex(b, 0, offset);
372 byte [] md5HashBytes = Bytes.toBytes(md5Hash);
373
374 if (md5HashBytes.length != MD5_HEX_LENGTH) {
375 LOG.error("MD5-hash length mismatch: Expected=" + MD5_HEX_LENGTH +
376 "; Got=" + md5HashBytes.length);
377 }
378
379
380 b[offset++] = ENC_SEPARATOR;
381 System.arraycopy(md5HashBytes, 0, b, offset, MD5_HEX_LENGTH);
382 offset += MD5_HEX_LENGTH;
383 b[offset++] = ENC_SEPARATOR;
384 }
385
386 return b;
387 }
388
389
390
391
392
393
394 public static byte [] getTableName(byte [] regionName) {
395 int offset = -1;
396 for (int i = 0; i < regionName.length; i++) {
397 if (regionName[i] == DELIMITER) {
398 offset = i;
399 break;
400 }
401 }
402 byte [] tableName = new byte[offset];
403 System.arraycopy(regionName, 0, tableName, 0, offset);
404 return tableName;
405 }
406
407
408
409
410
411
412 public static byte[] getStartKey(final byte[] regionName) throws IOException {
413 return parseRegionName(regionName)[1];
414 }
415
416
417
418
419
420
421
422 public static byte [][] parseRegionName(final byte [] regionName)
423 throws IOException {
424 int offset = -1;
425 for (int i = 0; i < regionName.length; i++) {
426 if (regionName[i] == DELIMITER) {
427 offset = i;
428 break;
429 }
430 }
431 if(offset == -1) throw new IOException("Invalid regionName format");
432 byte [] tableName = new byte[offset];
433 System.arraycopy(regionName, 0, tableName, 0, offset);
434 offset = -1;
435 for (int i = regionName.length - 1; i > 0; i--) {
436 if(regionName[i] == DELIMITER) {
437 offset = i;
438 break;
439 }
440 }
441 if(offset == -1) throw new IOException("Invalid regionName format");
442 byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
443 if(offset != tableName.length + 1) {
444 startKey = new byte[offset - tableName.length - 1];
445 System.arraycopy(regionName, tableName.length + 1, startKey, 0,
446 offset - tableName.length - 1);
447 }
448 byte [] id = new byte[regionName.length - offset - 1];
449 System.arraycopy(regionName, offset + 1, id, 0,
450 regionName.length - offset - 1);
451 byte [][] elements = new byte[3][];
452 elements[0] = tableName;
453 elements[1] = startKey;
454 elements[2] = id;
455 return elements;
456 }
457
458
459 public long getRegionId(){
460 return regionId;
461 }
462
463
464
465
466
467 public byte [] getRegionName(){
468 return regionName;
469 }
470
471
472
473
474 public String getRegionNameAsString() {
475 if (hasEncodedName(this.regionName)) {
476
477 return this.regionNameStr;
478 }
479
480
481
482
483 return this.regionNameStr + "." + this.getEncodedName();
484 }
485
486
487 public synchronized String getEncodedName() {
488 if (this.encodedName == NO_HASH) {
489 this.encodedName = encodeRegionName(this.regionName);
490 }
491 return this.encodedName;
492 }
493
494 public synchronized byte [] getEncodedNameAsBytes() {
495 if (this.encodedNameAsBytes == null) {
496 this.encodedNameAsBytes = Bytes.toBytes(getEncodedName());
497 }
498 return this.encodedNameAsBytes;
499 }
500
501
502 public byte [] getStartKey(){
503 return startKey;
504 }
505
506
507 public byte [] getEndKey(){
508 return endKey;
509 }
510
511
512
513
514
515 public byte[] getTableName() {
516 if (tableName == null || tableName.length == 0) {
517 tableName = getTableName(getRegionName());
518 }
519 return tableName;
520 }
521
522
523
524
525
526 public String getTableNameAsString() {
527 return Bytes.toString(tableName);
528 }
529
530
531
532
533
534
535
536
537 public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) {
538 if (Bytes.compareTo(rangeStartKey, rangeEndKey) > 0) {
539 throw new IllegalArgumentException(
540 "Invalid range: " + Bytes.toStringBinary(rangeStartKey) +
541 " > " + Bytes.toStringBinary(rangeEndKey));
542 }
543
544 boolean firstKeyInRange = Bytes.compareTo(rangeStartKey, startKey) >= 0;
545 boolean lastKeyInRange =
546 Bytes.compareTo(rangeEndKey, endKey) < 0 ||
547 Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY);
548 return firstKeyInRange && lastKeyInRange;
549 }
550
551
552
553
554 public boolean containsRow(byte[] row) {
555 return Bytes.compareTo(row, startKey) >= 0 &&
556 (Bytes.compareTo(row, endKey) < 0 ||
557 Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY));
558 }
559
560
561
562
563
564
565
566 @Deprecated
567 public HTableDescriptor getTableDesc() {
568 Configuration c = HBaseConfiguration.create();
569 c.set("fs.defaultFS", c.get(HConstants.HBASE_DIR));
570 c.set("fs.default.name", c.get(HConstants.HBASE_DIR));
571 FileSystem fs;
572 try {
573 fs = FileSystem.get(c);
574 } catch (IOException e) {
575 throw new RuntimeException(e);
576 }
577 FSTableDescriptors fstd =
578 new FSTableDescriptors(fs, new Path(c.get(HConstants.HBASE_DIR)));
579 try {
580 return fstd.get(this.tableName);
581 } catch (IOException e) {
582 throw new RuntimeException(e);
583 }
584 }
585
586
587
588
589
590 @Deprecated
591 public void setTableDesc(HTableDescriptor newDesc) {
592 Configuration c = HBaseConfiguration.create();
593 FileSystem fs;
594 try {
595 fs = FileSystem.get(c);
596 } catch (IOException e) {
597 throw new RuntimeException(e);
598 }
599 FSTableDescriptors fstd =
600 new FSTableDescriptors(fs, new Path(c.get(HConstants.HBASE_DIR)));
601 try {
602 fstd.add(newDesc);
603 } catch (IOException e) {
604 throw new RuntimeException(e);
605 }
606 }
607
608
609 public boolean isRootRegion() {
610 return Bytes.equals(tableName, HRegionInfo.ROOT_REGIONINFO.getTableName());
611 }
612
613
614
615
616 public boolean isMetaTable() {
617 return isRootRegion() || isMetaRegion();
618 }
619
620
621 public boolean isMetaRegion() {
622 return Bytes.equals(tableName, HRegionInfo.FIRST_META_REGIONINFO.getTableName());
623 }
624
625
626
627
628 public boolean isSplit() {
629 return this.split;
630 }
631
632
633
634
635 public void setSplit(boolean split) {
636 this.split = split;
637 }
638
639
640
641
642 public boolean isOffline() {
643 return this.offLine;
644 }
645
646
647
648
649
650
651 public void setOffline(boolean offLine) {
652 this.offLine = offLine;
653 }
654
655
656
657
658
659 public boolean isSplitParent() {
660 if (!isSplit()) return false;
661 if (!isOffline()) {
662 LOG.warn("Region is split but NOT offline: " + getRegionNameAsString());
663 }
664 return true;
665 }
666
667
668
669
670 @Override
671 public String toString() {
672 return "{" + HConstants.NAME + " => '" +
673 this.regionNameStr
674 + "', STARTKEY => '" +
675 Bytes.toStringBinary(this.startKey) + "', ENDKEY => '" +
676 Bytes.toStringBinary(this.endKey) +
677 "', ENCODED => " + getEncodedName() + "," +
678 (isOffline()? " OFFLINE => true,": "") +
679 (isSplit()? " SPLIT => true,": "") + "}";
680 }
681
682
683
684
685 @Override
686 public boolean equals(Object o) {
687 if (this == o) {
688 return true;
689 }
690 if (o == null) {
691 return false;
692 }
693 if (!(o instanceof HRegionInfo)) {
694 return false;
695 }
696 return this.compareTo((HRegionInfo)o) == 0;
697 }
698
699
700
701
702 @Override
703 public int hashCode() {
704 return this.hashCode;
705 }
706
707
708 @Override
709 public byte getVersion() {
710 return VERSION;
711 }
712
713
714
715
716
717 @Override
718 public void write(DataOutput out) throws IOException {
719 super.write(out);
720 Bytes.writeByteArray(out, endKey);
721 out.writeBoolean(offLine);
722 out.writeLong(regionId);
723 Bytes.writeByteArray(out, regionName);
724 out.writeBoolean(split);
725 Bytes.writeByteArray(out, startKey);
726 Bytes.writeByteArray(out, tableName);
727 out.writeInt(hashCode);
728 }
729
730 @Override
731 public void readFields(DataInput in) throws IOException {
732
733
734
735 byte version = in.readByte();
736 if (version == 0) {
737
738
739 this.endKey = Bytes.readByteArray(in);
740 this.offLine = in.readBoolean();
741 this.regionId = in.readLong();
742 this.regionName = Bytes.readByteArray(in);
743 this.regionNameStr = Bytes.toStringBinary(this.regionName);
744 this.split = in.readBoolean();
745 this.startKey = Bytes.readByteArray(in);
746 try {
747 HTableDescriptor htd = new HTableDescriptor();
748 htd.readFields(in);
749 this.tableName = htd.getName();
750 } catch(EOFException eofe) {
751 throw new IOException("HTD not found in input buffer", eofe);
752 }
753 this.hashCode = in.readInt();
754 } else if (getVersion() == version) {
755 this.endKey = Bytes.readByteArray(in);
756 this.offLine = in.readBoolean();
757 this.regionId = in.readLong();
758 this.regionName = Bytes.readByteArray(in);
759 this.regionNameStr = Bytes.toStringBinary(this.regionName);
760 this.split = in.readBoolean();
761 this.startKey = Bytes.readByteArray(in);
762 this.tableName = Bytes.readByteArray(in);
763 this.hashCode = in.readInt();
764 } else {
765 throw new IOException("Non-migratable/unknown version=" + getVersion());
766 }
767 }
768
769
770
771
772
773 public int compareTo(HRegionInfo o) {
774 if (o == null) {
775 return 1;
776 }
777
778
779 int result = Bytes.compareTo(this.tableName, o.tableName);
780 if (result != 0) {
781 return result;
782 }
783
784
785 result = Bytes.compareTo(this.startKey, o.startKey);
786 if (result != 0) {
787 return result;
788 }
789
790
791 result = Bytes.compareTo(this.endKey, o.endKey);
792
793 if (result != 0) {
794 if (this.getStartKey().length != 0
795 && this.getEndKey().length == 0) {
796 return 1;
797 }
798 if (o.getStartKey().length != 0
799 && o.getEndKey().length == 0) {
800 return -1;
801 }
802 return result;
803 }
804
805
806
807 if (this.regionId > o.regionId) {
808 return 1;
809 } else if (this.regionId < o.regionId) {
810 return -1;
811 }
812
813 if (this.offLine == o.offLine)
814 return 0;
815 if (this.offLine == true) return -1;
816
817 return 1;
818 }
819
820
821
822
823 public KVComparator getComparator() {
824 return isRootRegion()? KeyValue.ROOT_COMPARATOR: isMetaRegion()?
825 KeyValue.META_COMPARATOR: KeyValue.COMPARATOR;
826 }
827 }