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