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