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. nn * @return a RegionSplitPolicy n 097 */ 098 public static RegionSplitPolicy create(HRegion region, Configuration conf) throws IOException { 099 Preconditions.checkNotNull(region, "Region should not be null."); 100 Class<? extends RegionSplitPolicy> clazz = 101 getSplitPolicyClass(region.getTableDescriptor(), conf); 102 RegionSplitPolicy policy = ReflectionUtils.newInstance(clazz, conf); 103 policy.configureForRegion(region); 104 return policy; 105 } 106 107 public static Class<? extends RegionSplitPolicy> getSplitPolicyClass(TableDescriptor htd, 108 Configuration conf) throws IOException { 109 String className = htd.getRegionSplitPolicyClassName(); 110 if (className == null) { 111 className = 112 conf.get(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, DEFAULT_SPLIT_POLICY_CLASS.getName()); 113 } 114 115 try { 116 Class<? extends RegionSplitPolicy> clazz = 117 Class.forName(className).asSubclass(RegionSplitPolicy.class); 118 return clazz; 119 } catch (Exception e) { 120 throw new IOException("Unable to load configured region split policy '" + className 121 + "' for table '" + htd.getTableName() + "'", e); 122 } 123 } 124 125 /** 126 * In 127 * {@link HRegionFileSystem#splitStoreFile(org.apache.hadoop.hbase.client.RegionInfo, String, HStoreFile, byte[], boolean, RegionSplitPolicy)} 128 * we are not creating the split reference if split row does not lie inside the StoreFile range. 129 * But in some use cases we may need to create the split reference even when the split row does 130 * not lie inside the StoreFile range. This method can be used to decide, whether to skip the the 131 * StoreFile range check or not. 132 * <p> 133 * This method is not for general use. It is a mechanism put in place by Phoenix local indexing to 134 * defeat standard hbase behaviors. Phoenix local indices are very likely the only folks who would 135 * make use of this method. On the Master-side, we will instantiate a RegionSplitPolicy instance 136 * and run this method ONLY... none of the others make sense on the Master-side. 137 * </p> 138 * TODO: Shutdown this phoenix specialization or do it via some other means. 139 * @return whether to skip the StoreFile range check or not 140 */ 141 protected boolean skipStoreFileRangeCheck(String familyName) { 142 return false; 143 } 144}