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 String nameAsString = null; 068 private final TableName tableName; 069 070 private static int generateHashCode(final TableName tableName, final byte[] startKey, 071 final byte[] endKey, final long regionId, final int replicaId, boolean offLine, 072 byte[] regionName) { 073 int result = Arrays.hashCode(regionName); 074 result = (int) (result ^ regionId); 075 result ^= Arrays.hashCode(checkStartKey(startKey)); 076 result ^= Arrays.hashCode(checkEndKey(endKey)); 077 result ^= Boolean.valueOf(offLine).hashCode(); 078 result ^= Arrays.hashCode(tableName.getName()); 079 result ^= replicaId; 080 return result; 081 } 082 083 private static byte[] checkStartKey(byte[] startKey) { 084 return startKey == null ? HConstants.EMPTY_START_ROW : startKey; 085 } 086 087 private static byte[] checkEndKey(byte[] endKey) { 088 return endKey == null ? HConstants.EMPTY_END_ROW : endKey; 089 } 090 091 private static TableName checkTableName(TableName tableName) { 092 if (tableName == null) { 093 throw new IllegalArgumentException("TableName cannot be null"); 094 } 095 return tableName; 096 } 097 098 private static int checkReplicaId(int regionId) { 099 if (regionId > MAX_REPLICA_ID) { 100 throw new IllegalArgumentException("ReplicaId cannot be greater than " + MAX_REPLICA_ID); 101 } 102 return regionId; 103 } 104 105 /** 106 * Package private constructor used constructing MutableRegionInfo for the first meta regions 107 */ 108 MutableRegionInfo(long regionId, TableName tableName, int replicaId) { 109 this(tableName, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW, false, regionId, 110 replicaId, false); 111 } 112 113 MutableRegionInfo(final TableName tableName, final byte[] startKey, final byte[] endKey, 114 final boolean split, final long regionId, final int replicaId, boolean offLine) { 115 this.tableName = checkTableName(tableName); 116 this.startKey = checkStartKey(startKey); 117 this.endKey = checkEndKey(endKey); 118 this.split = split; 119 this.regionId = regionId; 120 this.replicaId = checkReplicaId(replicaId); 121 this.offLine = offLine; 122 this.regionName = RegionInfo.createRegionName(this.tableName, this.startKey, this.regionId, 123 this.replicaId, !this.tableName.equals(TableName.META_TABLE_NAME)); 124 this.encodedName = RegionInfo.encodeRegionName(this.regionName); 125 this.hashCode = generateHashCode(this.tableName, this.startKey, this.endKey, this.regionId, 126 this.replicaId, this.offLine, this.regionName); 127 this.encodedNameAsBytes = Bytes.toBytes(this.encodedName); 128 } 129 130 /** 131 * Returns Return a short, printable name for this region (usually encoded name) for us logging. 132 */ 133 @Override 134 public String getShortNameToLog() { 135 return RegionInfo.prettyPrint(this.getEncodedName()); 136 } 137 138 /** Returns the regionId */ 139 @Override 140 public long getRegionId() { 141 return regionId; 142 } 143 144 /** 145 * @return the regionName as an array of bytes. 146 * @see #getRegionNameAsString() 147 */ 148 @Override 149 public byte[] getRegionName() { 150 return regionName; 151 } 152 153 /** 154 * Returns region name as a String for use in logging, tracing, etc. Expensive enough to compute 155 * that we do it on first request and save it. Used often because it's included in trace of every 156 * RPC. 157 */ 158 @Override 159 public String getRegionNameAsString() { 160 if (nameAsString == null) { 161 String name = RegionInfo.getRegionNameAsString(this, this.regionName); 162 // may race with other threads setting this, but that's ok 163 nameAsString = name; 164 return name; 165 } else { 166 return nameAsString; 167 } 168 } 169 170 /** Returns the encoded region name */ 171 @Override 172 public String getEncodedName() { 173 return this.encodedName; 174 } 175 176 @Override 177 public byte[] getEncodedNameAsBytes() { 178 return this.encodedNameAsBytes; 179 } 180 181 /** Returns the startKey */ 182 @Override 183 public byte[] getStartKey() { 184 return startKey; 185 } 186 187 /** Returns the endKey */ 188 @Override 189 public byte[] getEndKey() { 190 return endKey; 191 } 192 193 /** 194 * Get current table name of the region 195 */ 196 @Override 197 public TableName getTable() { 198 return this.tableName; 199 } 200 201 /** 202 * Returns true if the given inclusive range of rows is fully contained by this region. For 203 * example, if the region is foo,a,g and this is passed ["b","c"] or ["a","c"] it will return 204 * true, but if this is passed ["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 CellComparator cellComparator = CellComparatorImpl.getCellComparator(tableName); 210 if (cellComparator.compareRows(rangeStartKey, rangeEndKey) > 0) { 211 throw new IllegalArgumentException("Invalid range: " + Bytes.toStringBinary(rangeStartKey) 212 + " > " + Bytes.toStringBinary(rangeEndKey)); 213 } 214 215 boolean firstKeyInRange = cellComparator.compareRows(rangeStartKey, startKey) >= 0; 216 boolean lastKeyInRange = cellComparator.compareRows(rangeEndKey, endKey) < 0 217 || Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY); 218 return firstKeyInRange && lastKeyInRange; 219 } 220 221 /** 222 * Return true if the given row falls in this region. 223 */ 224 @Override 225 public boolean containsRow(byte[] row) { 226 CellComparator cellComparator = CellComparatorImpl.getCellComparator(tableName); 227 return cellComparator.compareRows(row, startKey) >= 0 228 && (cellComparator.compareRows(row, endKey) < 0 229 || Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY)); 230 } 231 232 /** Returns 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 /** Returns True if has been split and has daughters. */ 239 @Override 240 public boolean isSplit() { 241 return this.split; 242 } 243 244 /** 245 * Change the split status flag. 246 * @param split set split status 247 */ 248 public MutableRegionInfo setSplit(boolean split) { 249 this.split = split; 250 return this; 251 } 252 253 /** 254 * @return True if this region is offline. 255 * @deprecated since 3.0.0 and will be removed in 4.0.0 256 * @see <a href="https://issues.apache.org/jira/browse/HBASE-25210">HBASE-25210</a> 257 */ 258 @Override 259 @Deprecated 260 public boolean isOffline() { 261 return this.offLine; 262 } 263 264 /** 265 * The parent of a region split is offline while split daughters hold references to the parent. 266 * Offlined regions are closed. 267 * @param offLine Set online/offline status. 268 */ 269 public MutableRegionInfo setOffline(boolean offLine) { 270 this.offLine = offLine; 271 return this; 272 } 273 274 /** 275 * @return True if this is a split parent region. 276 * @deprecated since 3.0.0 and will be removed in 4.0.0, Use {@link #isSplit()} instead. 277 * @see <a href="https://issues.apache.org/jira/browse/HBASE-25210">HBASE-25210</a> 278 */ 279 @Override 280 @Deprecated 281 public boolean isSplitParent() { 282 if (!isSplit()) { 283 return false; 284 } 285 if (!isOffline()) { 286 LOG.warn("Region is split but NOT offline: " + getRegionNameAsString()); 287 } 288 return true; 289 } 290 291 /** 292 * Returns the region replica id 293 * @return returns region replica id 294 */ 295 @Override 296 public int getReplicaId() { 297 return replicaId; 298 } 299 300 /** 301 * @see Object#toString() 302 */ 303 @Override 304 public String toString() { 305 return "{ENCODED => " + getEncodedName() + ", " + HConstants.NAME + " => '" 306 + Bytes.toStringBinary(this.regionName) + "', STARTKEY => '" 307 + Bytes.toStringBinary(this.startKey) + "', ENDKEY => '" + Bytes.toStringBinary(this.endKey) 308 + "'" + (isOffline() ? ", OFFLINE => true" : "") + (isSplit() ? ", SPLIT => true" : "") 309 + ((replicaId > 0) ? ", REPLICA_ID => " + replicaId : "") + "}"; 310 } 311 312 /** 313 * @see Object#equals(Object) 314 */ 315 @Override 316 public boolean equals(Object o) { 317 if (this == o) { 318 return true; 319 } 320 if (o == null) { 321 return false; 322 } 323 if (!(o instanceof RegionInfo)) { 324 return false; 325 } 326 return compareTo((RegionInfo) o) == 0; 327 } 328 329 /** 330 * @see Object#hashCode() 331 */ 332 @Override 333 public int hashCode() { 334 return this.hashCode; 335 } 336}