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 &lt; 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}