001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.client; 019 020import java.util.Arrays; 021import org.apache.hadoop.hbase.CellComparator; 022import org.apache.hadoop.hbase.CellComparatorImpl; 023import org.apache.hadoop.hbase.HConstants; 024import org.apache.hadoop.hbase.TableName; 025import org.apache.hadoop.hbase.util.Bytes; 026import org.apache.yetus.audience.InterfaceAudience; 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030/** 031 * An implementation of RegionInfo that adds mutable methods so can build a RegionInfo instance. 032 * Package private. Use {@link RegionInfoBuilder} creating instances of {@link RegionInfo}s. 033 */ 034@InterfaceAudience.Private 035class MutableRegionInfo implements RegionInfo { 036 private static final Logger LOG = LoggerFactory.getLogger(MutableRegionInfo.class); 037 private static final int MAX_REPLICA_ID = 0xFFFF; 038 039 /** 040 * The new format for a region name contains its encodedName at the end. The encoded name also 041 * serves as the directory name for the region in the filesystem. New region name format: 042 * <tablename>,,<startkey>,<regionIdTimestamp>.<encodedName>. where, <encodedName> 043 * is a hex version of the MD5 hash of <tablename>,<startkey>,<regionIdTimestamp> The old 044 * region name format: <tablename>,<startkey>,<regionIdTimestamp> For region names in the 045 * old format, the encoded name is a 32-bit JenkinsHash integer value (in its decimal notation, 046 * string form). 047 * <p> 048 * **NOTE** The first hbase:meta region, and regions created by an older version of HBase (0.20 or 049 * prior) will continue to use the old region name format. 050 */ 051 052 // This flag is in the parent of a split while the parent is still referenced by daughter 053 // regions. We USED to set this flag when we disabled a table but now table state is kept up in 054 // zookeeper as of 0.90.0 HBase. And now in DisableTableProcedure, finally we will create bunch 055 // of UnassignProcedures and at the last of the procedure we will set the region state to 056 // CLOSED, and will not change the offLine flag. 057 private boolean offLine; 058 private boolean split; 059 private final long regionId; 060 private final int replicaId; 061 private final byte[] regionName; 062 private final byte[] startKey; 063 private final byte[] endKey; 064 private final int hashCode; 065 private final String encodedName; 066 private final byte[] encodedNameAsBytes; 067 private final TableName tableName; 068 069 private static int generateHashCode(final TableName tableName, final byte[] startKey, 070 final byte[] endKey, final long regionId, final int replicaId, boolean offLine, 071 byte[] regionName) { 072 int result = Arrays.hashCode(regionName); 073 result = (int) (result ^ regionId); 074 result ^= Arrays.hashCode(checkStartKey(startKey)); 075 result ^= Arrays.hashCode(checkEndKey(endKey)); 076 result ^= Boolean.valueOf(offLine).hashCode(); 077 result ^= Arrays.hashCode(tableName.getName()); 078 result ^= replicaId; 079 return result; 080 } 081 082 private static byte[] checkStartKey(byte[] startKey) { 083 return startKey == null ? HConstants.EMPTY_START_ROW : startKey; 084 } 085 086 private static byte[] checkEndKey(byte[] endKey) { 087 return endKey == null ? HConstants.EMPTY_END_ROW : endKey; 088 } 089 090 private static TableName checkTableName(TableName tableName) { 091 if (tableName == null) { 092 throw new IllegalArgumentException("TableName cannot be null"); 093 } 094 return tableName; 095 } 096 097 private static int checkReplicaId(int regionId) { 098 if (regionId > MAX_REPLICA_ID) { 099 throw new IllegalArgumentException("ReplicaId cannot be greater than " + MAX_REPLICA_ID); 100 } 101 return regionId; 102 } 103 104 /** 105 * Package private constructor used constructing MutableRegionInfo for the first meta regions 106 */ 107 MutableRegionInfo(long regionId, TableName tableName, int replicaId) { 108 this(tableName, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW, false, regionId, 109 replicaId, false); 110 } 111 112 MutableRegionInfo(final TableName tableName, final byte[] startKey, final byte[] endKey, 113 final boolean split, final long regionId, final int replicaId, boolean offLine) { 114 this.tableName = checkTableName(tableName); 115 this.startKey = checkStartKey(startKey); 116 this.endKey = checkEndKey(endKey); 117 this.split = split; 118 this.regionId = regionId; 119 this.replicaId = checkReplicaId(replicaId); 120 this.offLine = offLine; 121 this.regionName = RegionInfo.createRegionName(this.tableName, this.startKey, this.regionId, 122 this.replicaId, !this.tableName.equals(TableName.META_TABLE_NAME)); 123 this.encodedName = RegionInfo.encodeRegionName(this.regionName); 124 this.hashCode = generateHashCode(this.tableName, this.startKey, this.endKey, this.regionId, 125 this.replicaId, this.offLine, this.regionName); 126 this.encodedNameAsBytes = Bytes.toBytes(this.encodedName); 127 } 128 129 /** 130 * Returns Return a short, printable name for this region (usually encoded name) for us logging. 131 */ 132 @Override 133 public String getShortNameToLog() { 134 return RegionInfo.prettyPrint(this.getEncodedName()); 135 } 136 137 /** Returns the regionId */ 138 @Override 139 public long getRegionId() { 140 return regionId; 141 } 142 143 /** 144 * @return the regionName as an array of bytes. 145 * @see #getRegionNameAsString() 146 */ 147 @Override 148 public byte[] getRegionName() { 149 return regionName; 150 } 151 152 /** Returns Region name as a String for use in logging, etc. */ 153 @Override 154 public String getRegionNameAsString() { 155 return RegionInfo.getRegionNameAsString(this, this.regionName); 156 } 157 158 /** Returns the encoded region name */ 159 @Override 160 public String getEncodedName() { 161 return this.encodedName; 162 } 163 164 @Override 165 public byte[] getEncodedNameAsBytes() { 166 return this.encodedNameAsBytes; 167 } 168 169 /** Returns the startKey */ 170 @Override 171 public byte[] getStartKey() { 172 return startKey; 173 } 174 175 /** Returns the endKey */ 176 @Override 177 public byte[] getEndKey() { 178 return endKey; 179 } 180 181 /** 182 * Get current table name of the region 183 */ 184 @Override 185 public TableName getTable() { 186 return this.tableName; 187 } 188 189 /** 190 * Returns true if the given inclusive range of rows is fully contained by this region. For 191 * example, if the region is foo,a,g and this is passed ["b","c"] or ["a","c"] it will return 192 * true, but if this is passed ["b","z"] it will return false. 193 * @throws IllegalArgumentException if the range passed is invalid (ie. end < start) 194 */ 195 @Override 196 public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) { 197 CellComparator cellComparator = CellComparatorImpl.getCellComparator(tableName); 198 if (cellComparator.compareRows(rangeStartKey, rangeEndKey) > 0) { 199 throw new IllegalArgumentException("Invalid range: " + Bytes.toStringBinary(rangeStartKey) 200 + " > " + Bytes.toStringBinary(rangeEndKey)); 201 } 202 203 boolean firstKeyInRange = cellComparator.compareRows(rangeStartKey, startKey) >= 0; 204 boolean lastKeyInRange = cellComparator.compareRows(rangeEndKey, endKey) < 0 205 || Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY); 206 return firstKeyInRange && lastKeyInRange; 207 } 208 209 /** 210 * Return true if the given row falls in this region. 211 */ 212 @Override 213 public boolean containsRow(byte[] row) { 214 CellComparator cellComparator = CellComparatorImpl.getCellComparator(tableName); 215 return cellComparator.compareRows(row, startKey) >= 0 216 && (cellComparator.compareRows(row, endKey) < 0 217 || Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY)); 218 } 219 220 /** Returns true if this region is a meta region */ 221 @Override 222 public boolean isMetaRegion() { 223 return tableName.equals(TableName.META_TABLE_NAME); 224 } 225 226 /** Returns True if has been split and has daughters. */ 227 @Override 228 public boolean isSplit() { 229 return this.split; 230 } 231 232 /** 233 * Change the split status flag. 234 * @param split set split status 235 */ 236 public MutableRegionInfo setSplit(boolean split) { 237 this.split = split; 238 return this; 239 } 240 241 /** 242 * @return True if this region is offline. 243 * @deprecated since 3.0.0 and will be removed in 4.0.0 244 * @see <a href="https://issues.apache.org/jira/browse/HBASE-25210">HBASE-25210</a> 245 */ 246 @Override 247 @Deprecated 248 public boolean isOffline() { 249 return this.offLine; 250 } 251 252 /** 253 * The parent of a region split is offline while split daughters hold references to the parent. 254 * Offlined regions are closed. 255 * @param offLine Set online/offline status. 256 */ 257 public MutableRegionInfo setOffline(boolean offLine) { 258 this.offLine = offLine; 259 return this; 260 } 261 262 /** 263 * @return True if this is a split parent region. 264 * @deprecated since 3.0.0 and will be removed in 4.0.0, Use {@link #isSplit()} instead. 265 * @see <a href="https://issues.apache.org/jira/browse/HBASE-25210">HBASE-25210</a> 266 */ 267 @Override 268 @Deprecated 269 public boolean isSplitParent() { 270 if (!isSplit()) { 271 return false; 272 } 273 if (!isOffline()) { 274 LOG.warn("Region is split but NOT offline: " + getRegionNameAsString()); 275 } 276 return true; 277 } 278 279 /** 280 * Returns the region replica id 281 * @return returns region replica id 282 */ 283 @Override 284 public int getReplicaId() { 285 return replicaId; 286 } 287 288 /** 289 * @see Object#toString() 290 */ 291 @Override 292 public String toString() { 293 return "{ENCODED => " + getEncodedName() + ", " + HConstants.NAME + " => '" 294 + Bytes.toStringBinary(this.regionName) + "', STARTKEY => '" 295 + Bytes.toStringBinary(this.startKey) + "', ENDKEY => '" + Bytes.toStringBinary(this.endKey) 296 + "'" + (isOffline() ? ", OFFLINE => true" : "") + (isSplit() ? ", SPLIT => true" : "") 297 + ((replicaId > 0) ? ", REPLICA_ID => " + replicaId : "") + "}"; 298 } 299 300 /** 301 * @see Object#equals(Object) 302 */ 303 @Override 304 public boolean equals(Object o) { 305 if (this == o) { 306 return true; 307 } 308 if (o == null) { 309 return false; 310 } 311 if (!(o instanceof RegionInfo)) { 312 return false; 313 } 314 return compareTo((RegionInfo) o) == 0; 315 } 316 317 /** 318 * @see Object#hashCode() 319 */ 320 @Override 321 public int hashCode() { 322 return this.hashCode; 323 } 324}