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