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}