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