3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 package org
.apache
.hadoop
.hbase
.client
;
21 import java
.util
.Arrays
;
23 import org
.apache
.commons
.lang3
.ArrayUtils
;
24 import org
.apache
.commons
.logging
.Log
;
25 import org
.apache
.commons
.logging
.LogFactory
;
26 import org
.apache
.hadoop
.hbase
.HConstants
;
27 import org
.apache
.hadoop
.hbase
.TableName
;
28 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
29 import org
.apache
.yetus
.audience
.InterfaceAudience
;
31 @InterfaceAudience.Private
32 public class RegionInfoBuilder
{
33 private static final Log LOG
= LogFactory
.getLog(RegionInfoBuilder
.class);
35 /** A non-capture group so that this can be embedded. */
36 public static final String ENCODED_REGION_NAME_REGEX
= "(?:[a-f0-9]+)";
38 private static final int MAX_REPLICA_ID
= 0xFFFF;
40 //TODO: Move NO_HASH to HStoreFile which is really the only place it is used.
41 public static final String NO_HASH
= null;
44 * RegionInfo for first meta region
45 * You cannot use this builder to make an instance of the {@link #FIRST_META_REGIONINFO}.
46 * Just refer to this instance. Also, while the instance is actually a MutableRI, its type is
47 * just RI so the mutable methods are not available (unless you go casting); it appears
48 * as immutable (I tried adding Immutable type but it just makes a mess).
50 // TODO: How come Meta regions still do not have encoded region names? Fix.
51 // hbase:meta,,1.1588230740 should be the hbase:meta first region name.
52 public static final RegionInfo FIRST_META_REGIONINFO
=
53 new MutableRegionInfo(1L, TableName
.META_TABLE_NAME
, RegionInfo
.DEFAULT_REPLICA_ID
);
55 private final TableName tableName
;
56 private byte[] startKey
= HConstants
.EMPTY_START_ROW
;
57 private byte[] endKey
= HConstants
.EMPTY_END_ROW
;
58 private long regionId
= System
.currentTimeMillis();
59 private int replicaId
= RegionInfo
.DEFAULT_REPLICA_ID
;
60 private boolean offLine
= false;
61 private boolean split
= false;
62 private byte[] regionName
= null;
63 private String encodedName
= null;
65 public static RegionInfoBuilder
newBuilder(TableName tableName
) {
66 return new RegionInfoBuilder(tableName
);
69 public static RegionInfoBuilder
newBuilder(RegionInfo regionInfo
) {
70 return new RegionInfoBuilder(regionInfo
);
73 private RegionInfoBuilder(TableName tableName
) {
74 this.tableName
= tableName
;
77 private RegionInfoBuilder(RegionInfo regionInfo
) {
78 this.tableName
= regionInfo
.getTable();
79 this.startKey
= regionInfo
.getStartKey();
80 this.endKey
= regionInfo
.getEndKey();
81 this.offLine
= regionInfo
.isOffline();
82 this.split
= regionInfo
.isSplit();
83 this.regionId
= regionInfo
.getRegionId();
84 this.replicaId
= regionInfo
.getReplicaId();
85 this.regionName
= regionInfo
.getRegionName();
86 this.encodedName
= regionInfo
.getEncodedName();
89 public RegionInfoBuilder
setStartKey(byte[] startKey
) {
90 this.startKey
= startKey
;
94 public RegionInfoBuilder
setEndKey(byte[] endKey
) {
99 public RegionInfoBuilder
setRegionId(long regionId
) {
100 this.regionId
= regionId
;
104 public RegionInfoBuilder
setReplicaId(int replicaId
) {
105 this.replicaId
= replicaId
;
109 public RegionInfoBuilder
setSplit(boolean split
) {
114 public RegionInfoBuilder
setOffline(boolean offLine
) {
115 this.offLine
= offLine
;
119 public RegionInfo
build() {
120 return new MutableRegionInfo(tableName
, startKey
, endKey
, split
,
121 regionId
, replicaId
, offLine
, regionName
, encodedName
);
125 * An implementation of RegionInfo that adds mutable methods so can build a RegionInfo instance.
127 @InterfaceAudience.Private
128 static class MutableRegionInfo
implements RegionInfo
, Comparable
<RegionInfo
> {
130 * The new format for a region name contains its encodedName at the end.
131 * The encoded name also serves as the directory name for the region
134 * New region name format:
135 * <tablename>,,<startkey>,<regionIdTimestamp>.<encodedName>.
137 * <encodedName> is a hex version of the MD5 hash of
138 * <tablename>,<startkey>,<regionIdTimestamp>
140 * The old region name format:
141 * <tablename>,<startkey>,<regionIdTimestamp>
142 * For region names in the old format, the encoded name is a 32-bit
143 * JenkinsHash integer value (in its decimal notation, string form).
147 * The first hbase:meta region, and regions created by an older
148 * version of HBase (0.20 or prior) will continue to use the
149 * old region name format.
152 // This flag is in the parent of a split while the parent is still referenced
153 // by daughter regions. We USED to set this flag when we disabled a table
154 // but now table state is kept up in zookeeper as of 0.90.0 HBase.
155 private boolean offLine
= false;
156 private boolean split
= false;
157 private final long regionId
;
158 private final int replicaId
;
159 private final byte[] regionName
;
160 private final byte[] startKey
;
161 private final byte[] endKey
;
162 private final int hashCode
;
163 private final String encodedName
;
164 private final byte[] encodedNameAsBytes
;
165 private final TableName tableName
;
167 private static int generateHashCode(final TableName tableName
, final byte[] startKey
,
168 final byte[] endKey
, final long regionId
,
169 final int replicaId
, boolean offLine
, byte[] regionName
) {
170 int result
= Arrays
.hashCode(regionName
);
171 result
= (int) (result ^ regionId
);
172 result ^
= Arrays
.hashCode(checkStartKey(startKey
));
173 result ^
= Arrays
.hashCode(checkEndKey(endKey
));
174 result ^
= Boolean
.valueOf(offLine
).hashCode();
175 result ^
= Arrays
.hashCode(tableName
.getName());
180 private static byte[] checkStartKey(byte[] startKey
) {
181 return startKey
== null? HConstants
.EMPTY_START_ROW
: startKey
;
184 private static byte[] checkEndKey(byte[] endKey
) {
185 return endKey
== null? HConstants
.EMPTY_END_ROW
: endKey
;
188 private static TableName
checkTableName(TableName tableName
) {
189 if (tableName
== null) {
190 throw new IllegalArgumentException("TableName cannot be null");
195 private static int checkReplicaId(int regionId
) {
196 if (regionId
> MAX_REPLICA_ID
) {
197 throw new IllegalArgumentException("ReplicaId cannot be greater than" + MAX_REPLICA_ID
);
203 * Private constructor used constructing MutableRegionInfo for the
206 private MutableRegionInfo(long regionId
, TableName tableName
, int replicaId
) {
208 HConstants
.EMPTY_START_ROW
,
209 HConstants
.EMPTY_END_ROW
,
214 RegionInfo
.createRegionName(tableName
, null, regionId
, replicaId
, false));
217 MutableRegionInfo(final TableName tableName
, final byte[] startKey
,
218 final byte[] endKey
, final boolean split
, final long regionId
,
219 final int replicaId
, boolean offLine
, byte[] regionName
) {
220 this(checkTableName(tableName
),
221 checkStartKey(startKey
),
224 checkReplicaId(replicaId
),
227 RegionInfo
.encodeRegionName(regionName
));
230 MutableRegionInfo(final TableName tableName
, final byte[] startKey
,
231 final byte[] endKey
, final boolean split
, final long regionId
,
232 final int replicaId
, boolean offLine
, byte[] regionName
, String encodedName
) {
233 this.tableName
= checkTableName(tableName
);
234 this.startKey
= checkStartKey(startKey
);
235 this.endKey
= checkEndKey(endKey
);
237 this.regionId
= regionId
;
238 this.replicaId
= checkReplicaId(replicaId
);
239 this.offLine
= offLine
;
240 if (ArrayUtils
.isEmpty(regionName
)) {
241 this.regionName
= RegionInfo
.createRegionName(this.tableName
, this.startKey
, this.regionId
, this.replicaId
,
242 !this.tableName
.equals(TableName
.META_TABLE_NAME
));
243 this.encodedName
= RegionInfo
.encodeRegionName(this.regionName
);
245 this.regionName
= regionName
;
246 this.encodedName
= encodedName
;
248 this.hashCode
= generateHashCode(
256 this.encodedNameAsBytes
= Bytes
.toBytes(this.encodedName
);
259 * @return Return a short, printable name for this region
260 * (usually encoded name) for us logging.
263 public String
getShortNameToLog() {
264 return RegionInfo
.prettyPrint(this.getEncodedName());
267 /** @return the regionId */
269 public long getRegionId(){
275 * @return the regionName as an array of bytes.
276 * @see #getRegionNameAsString()
279 public byte [] getRegionName(){
284 * @return Region name as a String for use in logging, etc.
287 public String
getRegionNameAsString() {
288 if (RegionInfo
.hasEncodedName(this.regionName
)) {
289 // new format region names already have their encoded name.
290 return Bytes
.toStringBinary(this.regionName
);
293 // old format. regionNameStr doesn't have the region name.
296 return Bytes
.toStringBinary(this.regionName
) + "." + this.getEncodedName();
299 /** @return the encoded region name */
301 public String
getEncodedName() {
302 return this.encodedName
;
306 public byte [] getEncodedNameAsBytes() {
307 return this.encodedNameAsBytes
;
310 /** @return the startKey */
312 public byte [] getStartKey(){
317 /** @return the endKey */
319 public byte [] getEndKey(){
324 * Get current table name of the region
328 public TableName
getTable() {
329 return this.tableName
;
333 * Returns true if the given inclusive range of rows is fully contained
334 * by this region. For example, if the region is foo,a,g and this is
335 * passed ["b","c"] or ["a","c"] it will return true, but if this is passed
336 * ["b","z"] it will return false.
337 * @throws IllegalArgumentException if the range passed is invalid (ie. end < start)
340 public boolean containsRange(byte[] rangeStartKey
, byte[] rangeEndKey
) {
341 if (Bytes
.compareTo(rangeStartKey
, rangeEndKey
) > 0) {
342 throw new IllegalArgumentException(
343 "Invalid range: " + Bytes
.toStringBinary(rangeStartKey
) +
344 " > " + Bytes
.toStringBinary(rangeEndKey
));
347 boolean firstKeyInRange
= Bytes
.compareTo(rangeStartKey
, startKey
) >= 0;
348 boolean lastKeyInRange
=
349 Bytes
.compareTo(rangeEndKey
, endKey
) < 0 ||
350 Bytes
.equals(endKey
, HConstants
.EMPTY_BYTE_ARRAY
);
351 return firstKeyInRange
&& lastKeyInRange
;
355 * Return true if the given row falls in this region.
358 public boolean containsRow(byte[] row
) {
359 return Bytes
.compareTo(row
, startKey
) >= 0 &&
360 (Bytes
.compareTo(row
, endKey
) < 0 ||
361 Bytes
.equals(endKey
, HConstants
.EMPTY_BYTE_ARRAY
));
364 /** @return true if this region is a meta region */
366 public boolean isMetaRegion() {
367 return tableName
.equals(FIRST_META_REGIONINFO
.getTable());
371 * @return True if has been split and has daughters.
374 public boolean isSplit() {
379 * @param split set split status
380 * @return MutableRegionInfo
382 public MutableRegionInfo
setSplit(boolean split
) {
388 * @return True if this region is offline.
391 public boolean isOffline() {
396 * The parent of a region split is offline while split daughters hold
397 * references to the parent. Offlined regions are closed.
398 * @param offLine Set online/offline status.
399 * @return MutableRegionInfo
401 public MutableRegionInfo
setOffline(boolean offLine
) {
402 this.offLine
= offLine
;
407 * @return True if this is a split parent region.
410 public boolean isSplitParent() {
411 if (!isSplit()) return false;
413 LOG
.warn("Region is split but NOT offline: " + getRegionNameAsString());
419 * Returns the region replica id
420 * @return returns region replica id
423 public int getReplicaId() {
428 * @see java.lang.Object#toString()
431 public String
toString() {
432 return "{ENCODED => " + getEncodedName() + ", " +
433 HConstants
.NAME
+ " => '" + Bytes
.toStringBinary(this.regionName
)
434 + "', STARTKEY => '" +
435 Bytes
.toStringBinary(this.startKey
) + "', ENDKEY => '" +
436 Bytes
.toStringBinary(this.endKey
) + "'" +
437 (isOffline()?
", OFFLINE => true": "") +
438 (isSplit()?
", SPLIT => true": "") +
439 ((replicaId
> 0)?
", REPLICA_ID => " + replicaId
: "") + "}";
444 * @see java.lang.Object#equals(java.lang.Object)
447 public boolean equals(Object o
) {
454 if (!(o
instanceof RegionInfo
)) {
457 return this.compareTo((RegionInfo
)o
) == 0;
461 * @see java.lang.Object#hashCode()
464 public int hashCode() {
465 return this.hashCode
;
469 public int compareTo(RegionInfo other
) {
470 return RegionInfo
.COMPARATOR
.compare(this, other
);