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
413 public static byte [][] parseRegionName(final byte [] regionName)
414 throws IOException {
415 int offset = -1;
416 for (int i = 0; i < regionName.length; i++) {
417 if (regionName[i] == DELIMITER) {
418 offset = i;
419 break;
420 }
421 }
422 if(offset == -1) throw new IOException("Invalid regionName format");
423 byte [] tableName = new byte[offset];
424 System.arraycopy(regionName, 0, tableName, 0, offset);
425 offset = -1;
426 for (int i = regionName.length - 1; i > 0; i--) {
427 if(regionName[i] == DELIMITER) {
428 offset = i;
429 break;
430 }
431 }
432 if(offset == -1) throw new IOException("Invalid regionName format");
433 byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
434 if(offset != tableName.length + 1) {
435 startKey = new byte[offset - tableName.length - 1];
436 System.arraycopy(regionName, tableName.length + 1, startKey, 0,
437 offset - tableName.length - 1);
438 }
439 byte [] id = new byte[regionName.length - offset - 1];
440 System.arraycopy(regionName, offset + 1, id, 0,
441 regionName.length - offset - 1);
442 byte [][] elements = new byte[3][];
443 elements[0] = tableName;
444 elements[1] = startKey;
445 elements[2] = id;
446 return elements;
447 }
448
449
450 public long getRegionId(){
451 return regionId;
452 }
453
454
455
456
457
458 public byte [] getRegionName(){
459 return regionName;
460 }
461
462
463
464
465 public String getRegionNameAsString() {
466 if (hasEncodedName(this.regionName)) {
467
468 return this.regionNameStr;
469 }
470
471
472
473
474 return this.regionNameStr + "." + this.getEncodedName();
475 }
476
477
478 public synchronized String getEncodedName() {
479 if (this.encodedName == NO_HASH) {
480 this.encodedName = encodeRegionName(this.regionName);
481 }
482 return this.encodedName;
483 }
484
485 public synchronized byte [] getEncodedNameAsBytes() {
486 if (this.encodedNameAsBytes == null) {
487 this.encodedNameAsBytes = Bytes.toBytes(getEncodedName());
488 }
489 return this.encodedNameAsBytes;
490 }
491
492
493 public byte [] getStartKey(){
494 return startKey;
495 }
496
497
498 public byte [] getEndKey(){
499 return endKey;
500 }
501
502
503
504
505
506 public byte[] getTableName() {
507 if (tableName == null || tableName.length == 0) {
508 tableName = getTableName(getRegionName());
509 }
510 return tableName;
511 }
512
513
514
515
516
517 public String getTableNameAsString() {
518 return Bytes.toString(tableName);
519 }
520
521
522
523
524
525
526
527
528 public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) {
529 if (Bytes.compareTo(rangeStartKey, rangeEndKey) > 0) {
530 throw new IllegalArgumentException(
531 "Invalid range: " + Bytes.toStringBinary(rangeStartKey) +
532 " > " + Bytes.toStringBinary(rangeEndKey));
533 }
534
535 boolean firstKeyInRange = Bytes.compareTo(rangeStartKey, startKey) >= 0;
536 boolean lastKeyInRange =
537 Bytes.compareTo(rangeEndKey, endKey) < 0 ||
538 Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY);
539 return firstKeyInRange && lastKeyInRange;
540 }
541
542
543
544
545 public boolean containsRow(byte[] row) {
546 return Bytes.compareTo(row, startKey) >= 0 &&
547 (Bytes.compareTo(row, endKey) < 0 ||
548 Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY));
549 }
550
551
552
553
554
555
556
557 @Deprecated
558 public HTableDescriptor getTableDesc() {
559 Configuration c = HBaseConfiguration.create();
560 c.set("fs.defaultFS", c.get(HConstants.HBASE_DIR));
561 c.set("fs.default.name", c.get(HConstants.HBASE_DIR));
562 FileSystem fs;
563 try {
564 fs = FileSystem.get(c);
565 } catch (IOException e) {
566 throw new RuntimeException(e);
567 }
568 FSTableDescriptors fstd =
569 new FSTableDescriptors(fs, new Path(c.get(HConstants.HBASE_DIR)));
570 try {
571 return fstd.get(this.tableName);
572 } catch (IOException e) {
573 throw new RuntimeException(e);
574 }
575 }
576
577
578
579
580
581 @Deprecated
582 public void setTableDesc(HTableDescriptor newDesc) {
583 Configuration c = HBaseConfiguration.create();
584 FileSystem fs;
585 try {
586 fs = FileSystem.get(c);
587 } catch (IOException e) {
588 throw new RuntimeException(e);
589 }
590 FSTableDescriptors fstd =
591 new FSTableDescriptors(fs, new Path(c.get(HConstants.HBASE_DIR)));
592 try {
593 fstd.add(newDesc);
594 } catch (IOException e) {
595 throw new RuntimeException(e);
596 }
597 }
598
599
600 public boolean isRootRegion() {
601 return Bytes.equals(tableName, HRegionInfo.ROOT_REGIONINFO.getTableName());
602 }
603
604
605
606
607 public boolean isMetaTable() {
608 return isRootRegion() || isMetaRegion();
609 }
610
611
612 public boolean isMetaRegion() {
613 return Bytes.equals(tableName, HRegionInfo.FIRST_META_REGIONINFO.getTableName());
614 }
615
616
617
618
619 public boolean isSplit() {
620 return this.split;
621 }
622
623
624
625
626 public void setSplit(boolean split) {
627 this.split = split;
628 }
629
630
631
632
633 public boolean isOffline() {
634 return this.offLine;
635 }
636
637
638
639
640
641
642 public void setOffline(boolean offLine) {
643 this.offLine = offLine;
644 }
645
646
647
648
649
650 public boolean isSplitParent() {
651 if (!isSplit()) return false;
652 if (!isOffline()) {
653 LOG.warn("Region is split but NOT offline: " + getRegionNameAsString());
654 }
655 return true;
656 }
657
658
659
660
661 @Override
662 public String toString() {
663 return "{" + HConstants.NAME + " => '" +
664 this.regionNameStr
665 + "', STARTKEY => '" +
666 Bytes.toStringBinary(this.startKey) + "', ENDKEY => '" +
667 Bytes.toStringBinary(this.endKey) +
668 "', ENCODED => " + getEncodedName() + "," +
669 (isOffline()? " OFFLINE => true,": "") +
670 (isSplit()? " SPLIT => true,": "") + "}";
671 }
672
673
674
675
676 @Override
677 public boolean equals(Object o) {
678 if (this == o) {
679 return true;
680 }
681 if (o == null) {
682 return false;
683 }
684 if (!(o instanceof HRegionInfo)) {
685 return false;
686 }
687 return this.compareTo((HRegionInfo)o) == 0;
688 }
689
690
691
692
693 @Override
694 public int hashCode() {
695 return this.hashCode;
696 }
697
698
699 @Override
700 public byte getVersion() {
701 return VERSION;
702 }
703
704
705
706
707
708 @Override
709 public void write(DataOutput out) throws IOException {
710 super.write(out);
711 Bytes.writeByteArray(out, endKey);
712 out.writeBoolean(offLine);
713 out.writeLong(regionId);
714 Bytes.writeByteArray(out, regionName);
715 out.writeBoolean(split);
716 Bytes.writeByteArray(out, startKey);
717 Bytes.writeByteArray(out, tableName);
718 out.writeInt(hashCode);
719 }
720
721 @Override
722 public void readFields(DataInput in) throws IOException {
723
724
725
726 byte version = in.readByte();
727 if (version == 0) {
728
729
730 this.endKey = Bytes.readByteArray(in);
731 this.offLine = in.readBoolean();
732 this.regionId = in.readLong();
733 this.regionName = Bytes.readByteArray(in);
734 this.regionNameStr = Bytes.toStringBinary(this.regionName);
735 this.split = in.readBoolean();
736 this.startKey = Bytes.readByteArray(in);
737 try {
738 HTableDescriptor htd = new HTableDescriptor();
739 htd.readFields(in);
740 this.tableName = htd.getName();
741 } catch(EOFException eofe) {
742 throw new IOException("HTD not found in input buffer", eofe);
743 }
744 this.hashCode = in.readInt();
745 } else if (getVersion() == version) {
746 this.endKey = Bytes.readByteArray(in);
747 this.offLine = in.readBoolean();
748 this.regionId = in.readLong();
749 this.regionName = Bytes.readByteArray(in);
750 this.regionNameStr = Bytes.toStringBinary(this.regionName);
751 this.split = in.readBoolean();
752 this.startKey = Bytes.readByteArray(in);
753 this.tableName = Bytes.readByteArray(in);
754 this.hashCode = in.readInt();
755 } else {
756 throw new IOException("Non-migratable/unknown version=" + getVersion());
757 }
758 }
759
760
761
762
763
764 public int compareTo(HRegionInfo o) {
765 if (o == null) {
766 return 1;
767 }
768
769
770 int result = Bytes.compareTo(this.tableName, o.tableName);
771 if (result != 0) {
772 return result;
773 }
774
775
776 result = Bytes.compareTo(this.startKey, o.startKey);
777 if (result != 0) {
778 return result;
779 }
780
781
782 result = Bytes.compareTo(this.endKey, o.endKey);
783
784 if (result != 0) {
785 if (this.getStartKey().length != 0
786 && this.getEndKey().length == 0) {
787 return 1;
788 }
789 if (o.getStartKey().length != 0
790 && o.getEndKey().length == 0) {
791 return -1;
792 }
793 return result;
794 }
795
796
797
798 if (this.regionId > o.regionId) {
799 return 1;
800 } else if (this.regionId < o.regionId) {
801 return -1;
802 }
803
804 if (this.offLine == o.offLine)
805 return 0;
806 if (this.offLine == true) return -1;
807
808 return 1;
809 }
810
811
812
813
814 public KVComparator getComparator() {
815 return isRootRegion()? KeyValue.ROOT_COMPARATOR: isMetaRegion()?
816 KeyValue.META_COMPARATOR: KeyValue.COMPARATOR;
817 }
818 }