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 */
018
019package org.apache.hadoop.hbase.client;
020
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Iterator;
024import java.util.List;
025
026import org.apache.hadoop.hbase.util.Bytes;
027import org.apache.yetus.audience.InterfaceAudience;
028
029/**
030 * Utility methods which contain the logic for regions and replicas.
031 */
032@InterfaceAudience.Private
033public class RegionReplicaUtil {
034
035  /**
036   * Whether or not the secondary region will wait for observing a flush / region open event
037   * from the primary region via async wal replication before enabling read requests. Since replayed
038   * edits from async wal replication from primary is not persisted in WAL, the memstore of the
039   * secondary region might be non-empty at the time of close or crash. For ensuring seqId's not
040   * "going back in time" in the secondary region replica, this should be enabled. However, in some
041   * cases the above semantics might be ok for some application classes.
042   * See HBASE-11580 for more context.
043   */
044  public static final String REGION_REPLICA_WAIT_FOR_PRIMARY_FLUSH_CONF_KEY
045    = "hbase.region.replica.wait.for.primary.flush";
046  protected static final boolean DEFAULT_REGION_REPLICA_WAIT_FOR_PRIMARY_FLUSH = true;
047
048  /**
049   * The default replicaId for the region
050   */
051  static final int DEFAULT_REPLICA_ID = 0;
052
053  /**
054   * Returns the RegionInfo for the given replicaId.
055   * RegionInfo's correspond to a range of a table, but more than one
056   * "instance" of the same range can be deployed which are differentiated by
057   * the replicaId.
058   * @param regionInfo
059   * @param replicaId the replicaId to use
060   * @return an RegionInfo object corresponding to the same range (table, start and
061   * end key), but for the given replicaId.
062   */
063  public static RegionInfo getRegionInfoForReplica(RegionInfo regionInfo, int replicaId) {
064    if (regionInfo.getReplicaId() == replicaId) {
065      return regionInfo;
066    }
067    return RegionInfoBuilder.newBuilder(regionInfo).setReplicaId(replicaId).build();
068  }
069
070  /**
071   * Returns the RegionInfo for the default replicaId (0). RegionInfo's correspond to
072   * a range of a table, but more than one "instance" of the same range can be
073   * deployed which are differentiated by the replicaId.
074   * @return an RegionInfo object corresponding to the same range (table, start and
075   * end key), but for the default replicaId.
076   */
077  public static RegionInfo getRegionInfoForDefaultReplica(RegionInfo regionInfo) {
078    return getRegionInfoForReplica(regionInfo, DEFAULT_REPLICA_ID);
079  }
080
081  /** @return true if this replicaId corresponds to default replica for the region */
082  public static boolean isDefaultReplica(int replicaId) {
083    return DEFAULT_REPLICA_ID == replicaId;
084  }
085
086  /** @return true if this region is a default replica for the region */
087  public static boolean isDefaultReplica(RegionInfo hri) {
088    return  hri.getReplicaId() == DEFAULT_REPLICA_ID;
089  }
090
091  /**
092   * Removes the non-default replicas from the passed regions collection
093   * @param regions
094   */
095  public static void removeNonDefaultRegions(Collection<RegionInfo> regions) {
096    Iterator<RegionInfo> iterator = regions.iterator();
097    while (iterator.hasNext()) {
098      RegionInfo hri = iterator.next();
099      if (!RegionReplicaUtil.isDefaultReplica(hri)) {
100        iterator.remove();
101      }
102    }
103  }
104
105  public static boolean isReplicasForSameRegion(RegionInfo regionInfoA, RegionInfo regionInfoB) {
106    return compareRegionInfosWithoutReplicaId(regionInfoA, regionInfoB) == 0;
107  }
108
109  private static int compareRegionInfosWithoutReplicaId(RegionInfo regionInfoA,
110      RegionInfo regionInfoB) {
111    int result = regionInfoA.getTable().compareTo(regionInfoB.getTable());
112    if (result != 0) {
113      return result;
114    }
115
116    // Compare start keys.
117    result = Bytes.compareTo(regionInfoA.getStartKey(), regionInfoB.getStartKey());
118    if (result != 0) {
119      return result;
120    }
121
122    // Compare end keys.
123    result = Bytes.compareTo(regionInfoA.getEndKey(), regionInfoB.getEndKey());
124
125    if (result != 0) {
126      if (regionInfoA.getStartKey().length != 0
127              && regionInfoA.getEndKey().length == 0) {
128          return 1; // this is last region
129      }
130      if (regionInfoB.getStartKey().length != 0
131              && regionInfoB.getEndKey().length == 0) {
132          return -1; // o is the last region
133      }
134      return result;
135    }
136
137    // regionId is usually milli timestamp -- this defines older stamps
138    // to be "smaller" than newer stamps in sort order.
139    if (regionInfoA.getRegionId() > regionInfoB.getRegionId()) {
140      return 1;
141    } else if (regionInfoA.getRegionId() < regionInfoB.getRegionId()) {
142      return -1;
143    }
144    return 0;
145  }
146
147  /**
148   * Create any replicas for the regions (the default replicas that was already created is passed to
149   * the method)
150   * @param tableDescriptor descriptor to use
151   * @param regions existing regions
152   * @param oldReplicaCount existing replica count
153   * @param newReplicaCount updated replica count due to modify table
154   * @return the combined list of default and non-default replicas
155   */
156  public static List<RegionInfo> addReplicas(final TableDescriptor tableDescriptor,
157      final List<RegionInfo> regions, int oldReplicaCount, int newReplicaCount) {
158    if ((newReplicaCount - 1) <= 0) {
159      return regions;
160    }
161    List<RegionInfo> hRegionInfos = new ArrayList<>((newReplicaCount) * regions.size());
162    for (RegionInfo ri : regions) {
163      if (RegionReplicaUtil.isDefaultReplica(ri) &&
164        (!ri.isOffline() || (!ri.isSplit() && !ri.isSplitParent()))) {
165        // region level replica index starts from 0. So if oldReplicaCount was 2 then the max replicaId for
166        // the existing regions would be 1
167        for (int j = oldReplicaCount; j < newReplicaCount; j++) {
168          hRegionInfos.add(RegionReplicaUtil.getRegionInfoForReplica(ri, j));
169        }
170      }
171    }
172    hRegionInfos.addAll(regions);
173    return hRegionInfos;
174  }
175}