| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.hadoop.hbase.client; |
| |
| import java.util.Arrays; |
| import org.apache.hadoop.hbase.CellComparator; |
| import org.apache.hadoop.hbase.CellComparatorImpl; |
| import org.apache.hadoop.hbase.HConstants; |
| import org.apache.hadoop.hbase.TableName; |
| import org.apache.hadoop.hbase.util.Bytes; |
| import org.apache.yetus.audience.InterfaceAudience; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * An implementation of RegionInfo that adds mutable methods so can build a RegionInfo instance. |
| * Package private. Use {@link RegionInfoBuilder} creating instances of {@link RegionInfo}s. |
| */ |
| @InterfaceAudience.Private |
| class MutableRegionInfo implements RegionInfo { |
| private static final Logger LOG = LoggerFactory.getLogger(MutableRegionInfo.class); |
| private static final int MAX_REPLICA_ID = 0xFFFF; |
| |
| /** |
| * The new format for a region name contains its encodedName at the end. The encoded name also |
| * serves as the directory name for the region in the filesystem. New region name format: |
| * <tablename>,,<startkey>,<regionIdTimestamp>.<encodedName>. where, <encodedName> |
| * is a hex version of the MD5 hash of <tablename>,<startkey>,<regionIdTimestamp> The old |
| * region name format: <tablename>,<startkey>,<regionIdTimestamp> For region names in the |
| * old format, the encoded name is a 32-bit JenkinsHash integer value (in its decimal notation, |
| * string form). |
| * <p> |
| * **NOTE** The first hbase:meta region, and regions created by an older version of HBase (0.20 or |
| * prior) will continue to use the old region name format. |
| */ |
| |
| // This flag is in the parent of a split while the parent is still referenced by daughter |
| // regions. We USED to set this flag when we disabled a table but now table state is kept up in |
| // zookeeper as of 0.90.0 HBase. And now in DisableTableProcedure, finally we will create bunch |
| // of UnassignProcedures and at the last of the procedure we will set the region state to |
| // CLOSED, and will not change the offLine flag. |
| private boolean offLine; |
| private boolean split; |
| private final long regionId; |
| private final int replicaId; |
| private final byte[] regionName; |
| private final byte[] startKey; |
| private final byte[] endKey; |
| private final int hashCode; |
| private final String encodedName; |
| private final byte[] encodedNameAsBytes; |
| private final TableName tableName; |
| |
| private static int generateHashCode(final TableName tableName, final byte[] startKey, |
| final byte[] endKey, final long regionId, final int replicaId, boolean offLine, |
| byte[] regionName) { |
| int result = Arrays.hashCode(regionName); |
| result = (int) (result ^ regionId); |
| result ^= Arrays.hashCode(checkStartKey(startKey)); |
| result ^= Arrays.hashCode(checkEndKey(endKey)); |
| result ^= Boolean.valueOf(offLine).hashCode(); |
| result ^= Arrays.hashCode(tableName.getName()); |
| result ^= replicaId; |
| return result; |
| } |
| |
| private static byte[] checkStartKey(byte[] startKey) { |
| return startKey == null ? HConstants.EMPTY_START_ROW : startKey; |
| } |
| |
| private static byte[] checkEndKey(byte[] endKey) { |
| return endKey == null ? HConstants.EMPTY_END_ROW : endKey; |
| } |
| |
| private static TableName checkTableName(TableName tableName) { |
| if (tableName == null) { |
| throw new IllegalArgumentException("TableName cannot be null"); |
| } |
| return tableName; |
| } |
| |
| private static int checkReplicaId(int regionId) { |
| if (regionId > MAX_REPLICA_ID) { |
| throw new IllegalArgumentException("ReplicaId cannot be greater than " + MAX_REPLICA_ID); |
| } |
| return regionId; |
| } |
| |
| /** |
| * Package private constructor used constructing MutableRegionInfo for the first meta regions |
| */ |
| MutableRegionInfo(long regionId, TableName tableName, int replicaId) { |
| this(tableName, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW, false, regionId, |
| replicaId, false); |
| } |
| |
| MutableRegionInfo(final TableName tableName, final byte[] startKey, final byte[] endKey, |
| final boolean split, final long regionId, final int replicaId, boolean offLine) { |
| this.tableName = checkTableName(tableName); |
| this.startKey = checkStartKey(startKey); |
| this.endKey = checkEndKey(endKey); |
| this.split = split; |
| this.regionId = regionId; |
| this.replicaId = checkReplicaId(replicaId); |
| this.offLine = offLine; |
| this.regionName = RegionInfo.createRegionName(this.tableName, this.startKey, this.regionId, |
| this.replicaId, !this.tableName.equals(TableName.META_TABLE_NAME)); |
| this.encodedName = RegionInfo.encodeRegionName(this.regionName); |
| this.hashCode = generateHashCode(this.tableName, this.startKey, this.endKey, this.regionId, |
| this.replicaId, this.offLine, this.regionName); |
| this.encodedNameAsBytes = Bytes.toBytes(this.encodedName); |
| } |
| |
| /** |
| * Returns Return a short, printable name for this region (usually encoded name) for us logging. |
| */ |
| @Override |
| public String getShortNameToLog() { |
| return RegionInfo.prettyPrint(this.getEncodedName()); |
| } |
| |
| /** Returns the regionId */ |
| @Override |
| public long getRegionId() { |
| return regionId; |
| } |
| |
| /** |
| * @return the regionName as an array of bytes. |
| * @see #getRegionNameAsString() |
| */ |
| @Override |
| public byte[] getRegionName() { |
| return regionName; |
| } |
| |
| /** Returns Region name as a String for use in logging, etc. */ |
| @Override |
| public String getRegionNameAsString() { |
| return RegionInfo.getRegionNameAsString(this, this.regionName); |
| } |
| |
| /** Returns the encoded region name */ |
| @Override |
| public String getEncodedName() { |
| return this.encodedName; |
| } |
| |
| @Override |
| public byte[] getEncodedNameAsBytes() { |
| return this.encodedNameAsBytes; |
| } |
| |
| /** Returns the startKey */ |
| @Override |
| public byte[] getStartKey() { |
| return startKey; |
| } |
| |
| /** Returns the endKey */ |
| @Override |
| public byte[] getEndKey() { |
| return endKey; |
| } |
| |
| /** |
| * Get current table name of the region |
| */ |
| @Override |
| public TableName getTable() { |
| return this.tableName; |
| } |
| |
| /** |
| * Returns true if the given inclusive range of rows is fully contained by this region. For |
| * example, if the region is foo,a,g and this is passed ["b","c"] or ["a","c"] it will return |
| * true, but if this is passed ["b","z"] it will return false. |
| * @throws IllegalArgumentException if the range passed is invalid (ie. end < start) |
| */ |
| @Override |
| public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) { |
| CellComparator cellComparator = CellComparatorImpl.getCellComparator(tableName); |
| if (cellComparator.compareRows(rangeStartKey, rangeEndKey) > 0) { |
| throw new IllegalArgumentException("Invalid range: " + Bytes.toStringBinary(rangeStartKey) |
| + " > " + Bytes.toStringBinary(rangeEndKey)); |
| } |
| |
| boolean firstKeyInRange = cellComparator.compareRows(rangeStartKey, startKey) >= 0; |
| boolean lastKeyInRange = cellComparator.compareRows(rangeEndKey, endKey) < 0 |
| || Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY); |
| return firstKeyInRange && lastKeyInRange; |
| } |
| |
| /** |
| * Return true if the given row falls in this region. |
| */ |
| @Override |
| public boolean containsRow(byte[] row) { |
| CellComparator cellComparator = CellComparatorImpl.getCellComparator(tableName); |
| return cellComparator.compareRows(row, startKey) >= 0 |
| && (cellComparator.compareRows(row, endKey) < 0 |
| || Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY)); |
| } |
| |
| /** Returns true if this region is a meta region */ |
| @Override |
| public boolean isMetaRegion() { |
| return tableName.equals(TableName.META_TABLE_NAME); |
| } |
| |
| /** Returns True if has been split and has daughters. */ |
| @Override |
| public boolean isSplit() { |
| return this.split; |
| } |
| |
| /** |
| * Change the split status flag. |
| * @param split set split status |
| */ |
| public MutableRegionInfo setSplit(boolean split) { |
| this.split = split; |
| return this; |
| } |
| |
| /** |
| * @return True if this region is offline. |
| * @deprecated since 3.0.0 and will be removed in 4.0.0 |
| * @see <a href="https://issues.apache.org/jira/browse/HBASE-25210">HBASE-25210</a> |
| */ |
| @Override |
| @Deprecated |
| public boolean isOffline() { |
| return this.offLine; |
| } |
| |
| /** |
| * The parent of a region split is offline while split daughters hold references to the parent. |
| * Offlined regions are closed. |
| * @param offLine Set online/offline status. |
| */ |
| public MutableRegionInfo setOffline(boolean offLine) { |
| this.offLine = offLine; |
| return this; |
| } |
| |
| /** |
| * @return True if this is a split parent region. |
| * @deprecated since 3.0.0 and will be removed in 4.0.0, Use {@link #isSplit()} instead. |
| * @see <a href="https://issues.apache.org/jira/browse/HBASE-25210">HBASE-25210</a> |
| */ |
| @Override |
| @Deprecated |
| public boolean isSplitParent() { |
| if (!isSplit()) { |
| return false; |
| } |
| if (!isOffline()) { |
| LOG.warn("Region is split but NOT offline: " + getRegionNameAsString()); |
| } |
| return true; |
| } |
| |
| /** |
| * Returns the region replica id |
| * @return returns region replica id |
| */ |
| @Override |
| public int getReplicaId() { |
| return replicaId; |
| } |
| |
| /** |
| * @see Object#toString() |
| */ |
| @Override |
| public String toString() { |
| return "{ENCODED => " + getEncodedName() + ", " + HConstants.NAME + " => '" |
| + Bytes.toStringBinary(this.regionName) + "', STARTKEY => '" |
| + Bytes.toStringBinary(this.startKey) + "', ENDKEY => '" + Bytes.toStringBinary(this.endKey) |
| + "'" + (isOffline() ? ", OFFLINE => true" : "") + (isSplit() ? ", SPLIT => true" : "") |
| + ((replicaId > 0) ? ", REPLICA_ID => " + replicaId : "") + "}"; |
| } |
| |
| /** |
| * @see Object#equals(Object) |
| */ |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (o == null) { |
| return false; |
| } |
| if (!(o instanceof RegionInfo)) { |
| return false; |
| } |
| return compareTo((RegionInfo) o) == 0; |
| } |
| |
| /** |
| * @see Object#hashCode() |
| */ |
| @Override |
| public int hashCode() { |
| return this.hashCode; |
| } |
| } |