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.regionserver;
019
020import java.io.IOException;
021import java.util.List;
022import java.util.Optional;
023
024import org.apache.hadoop.conf.Configuration;
025import org.apache.hadoop.conf.Configured;
026import org.apache.hadoop.hbase.HBaseInterfaceAudience;
027import org.apache.hadoop.hbase.HConstants;
028import org.apache.hadoop.hbase.TableName;
029import org.apache.hadoop.hbase.client.TableDescriptor;
030import org.apache.hadoop.util.ReflectionUtils;
031import org.apache.yetus.audience.InterfaceAudience;
032import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
033
034
035/**
036 * A split policy determines when a Region should be split.
037 *
038 * @see SteppingSplitPolicy Default split policy since 2.0.0
039 * @see IncreasingToUpperBoundRegionSplitPolicy Default split policy since
040 *      0.94.0
041 * @see ConstantSizeRegionSplitPolicy Default split policy before 0.94.0
042 */
043@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
044public abstract class RegionSplitPolicy extends Configured {
045  private static final Class<? extends RegionSplitPolicy>
046    DEFAULT_SPLIT_POLICY_CLASS = SteppingSplitPolicy.class;
047
048  /**
049   * The region configured for this split policy.
050   * As of hbase-2.0.0, RegionSplitPolicy can be instantiated on the Master-side so the
051   * Phoenix local-indexer can block default hbase behavior. This is an exotic usage. Should not
052   * trouble any other users of RegionSplitPolicy.
053   */
054  protected HRegion region;
055
056  /**
057   * Upon construction, this method will be called with the region
058   * to be governed. It will be called once and only once.
059   */
060  protected void configureForRegion(HRegion region) {
061    Preconditions.checkState(
062        this.region == null,
063        "Policy already configured for region {}",
064        this.region);
065
066    this.region = region;
067  }
068
069  /**
070   * @return true if the specified region should be split.
071   */
072  protected abstract boolean shouldSplit();
073
074  /**
075   * @return {@code true} if the specified region can be split.
076   */
077  protected boolean canSplit() {
078    return !region.getRegionInfo().isMetaRegion() && region.isAvailable() &&
079      !TableName.NAMESPACE_TABLE_NAME.equals(region.getRegionInfo().getTable()) &&
080      region.getStores().stream().allMatch(HStore::canSplit);
081  }
082
083  /**
084   * @return the key at which the region should be split, or null
085   * if it cannot be split. This will only be called if shouldSplit
086   * previously returned true.
087   */
088  protected byte[] getSplitPoint() {
089    List<HStore> stores = region.getStores();
090
091    byte[] splitPointFromLargestStore = null;
092    long largestStoreSize = 0;
093    for (HStore s : stores) {
094      Optional<byte[]> splitPoint = s.getSplitPoint();
095      // Store also returns null if it has references as way of indicating it is not splittable
096      long storeSize = s.getSize();
097      if (splitPoint.isPresent() && largestStoreSize < storeSize) {
098        splitPointFromLargestStore = splitPoint.get();
099        largestStoreSize = storeSize;
100      }
101    }
102
103    return splitPointFromLargestStore;
104  }
105
106  /**
107   * Create the RegionSplitPolicy configured for the given table.
108   * @param region
109   * @param conf
110   * @return a RegionSplitPolicy
111   * @throws IOException
112   */
113  public static RegionSplitPolicy create(HRegion region,
114      Configuration conf) throws IOException {
115    Preconditions.checkNotNull(region, "Region should not be null.");
116    Class<? extends RegionSplitPolicy> clazz = getSplitPolicyClass(
117        region.getTableDescriptor(), conf);
118    RegionSplitPolicy policy = ReflectionUtils.newInstance(clazz, conf);
119    policy.configureForRegion(region);
120    return policy;
121  }
122
123  public static Class<? extends RegionSplitPolicy> getSplitPolicyClass(
124      TableDescriptor htd, Configuration conf) throws IOException {
125    String className = htd.getRegionSplitPolicyClassName();
126    if (className == null) {
127      className = conf.get(HConstants.HBASE_REGION_SPLIT_POLICY_KEY,
128          DEFAULT_SPLIT_POLICY_CLASS.getName());
129    }
130
131    try {
132      Class<? extends RegionSplitPolicy> clazz =
133        Class.forName(className).asSubclass(RegionSplitPolicy.class);
134      return clazz;
135    } catch (Exception  e) {
136      throw new IOException(
137          "Unable to load configured region split policy '" +
138          className + "' for table '" + htd.getTableName() + "'",
139          e);
140    }
141  }
142
143  /**
144   * In {@link HRegionFileSystem#splitStoreFile(org.apache.hadoop.hbase.client.RegionInfo, String,
145   * HStoreFile, byte[], boolean, RegionSplitPolicy)} we are not creating the split reference
146   * if split row does not lie inside the StoreFile range. But in some use cases we may need to
147   * create the split reference even when the split row does not lie inside the StoreFile range.
148   * This method can be used to decide, whether to skip the the StoreFile range check or not.
149   *
150   * <p>This method is not for general use. It is a mechanism put in place by Phoenix
151   * local indexing to defeat standard hbase behaviors. Phoenix local indices are very likely
152   * the only folks who would make use of this method. On the Master-side, we will instantiate
153   * a RegionSplitPolicy instance and run this method ONLY... none of the others make sense
154   * on the Master-side.</p>
155   *
156   * TODO: Shutdown this phoenix specialization or do it via some other means.
157   * @return whether to skip the StoreFile range check or not
158   */
159  protected boolean skipStoreFileRangeCheck(String familyName) {
160    return false;
161  }
162}