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.util.concurrent.ThreadLocalRandom;
021import org.apache.hadoop.conf.Configuration;
022import org.apache.hadoop.hbase.HBaseInterfaceAudience;
023import org.apache.hadoop.hbase.HConstants;
024import org.apache.hadoop.hbase.client.TableDescriptor;
025import org.apache.hadoop.hbase.conf.ConfigKey;
026import org.apache.hadoop.hbase.procedure2.util.StringUtils;
027import org.apache.yetus.audience.InterfaceAudience;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031/**
032 * A {@link RegionSplitPolicy} implementation which splits a region as soon as any of its store
033 * files exceeds a maximum configurable size.
034 * <p>
035 * This is the default split policy. From 0.94.0 on the default split policy has changed to
036 * {@link IncreasingToUpperBoundRegionSplitPolicy}
037 * </p>
038 */
039@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
040public class ConstantSizeRegionSplitPolicy extends RegionSplitPolicy {
041  private static final Logger LOG = LoggerFactory.getLogger(ConstantSizeRegionSplitPolicy.class);
042  private long desiredMaxFileSize;
043  private double jitterRate;
044  protected boolean overallHRegionFiles;
045
046  public static final String MAX_FILESIZE_JITTER_KEY =
047    ConfigKey.DOUBLE("hbase.hregion.max.filesize.jitter");
048
049  @Override
050  public String toString() {
051    return "ConstantSizeRegionSplitPolicy{" + "desiredMaxFileSize=" + desiredMaxFileSize
052      + ", jitterRate=" + jitterRate + '}';
053  }
054
055  @Override
056  protected void configureForRegion(HRegion region) {
057    super.configureForRegion(region);
058    Configuration conf = getConf();
059    TableDescriptor desc = region.getTableDescriptor();
060    if (desc != null) {
061      this.desiredMaxFileSize = desc.getMaxFileSize();
062    }
063    if (this.desiredMaxFileSize <= 0) {
064      this.desiredMaxFileSize =
065        conf.getLong(HConstants.HREGION_MAX_FILESIZE, HConstants.DEFAULT_MAX_FILE_SIZE);
066    }
067    this.overallHRegionFiles =
068      conf.getBoolean(HConstants.OVERALL_HREGION_FILES, HConstants.DEFAULT_OVERALL_HREGION_FILES);
069    double jitter = conf.getDouble(MAX_FILESIZE_JITTER_KEY, 0.25D);
070    this.jitterRate = (ThreadLocalRandom.current().nextFloat() - 0.5D) * jitter;
071    long jitterValue = (long) (this.desiredMaxFileSize * this.jitterRate);
072    // Default jitter is ~12% +/-. Make sure the long value won't overflow with jitter
073    if (this.jitterRate > 0 && jitterValue > (Long.MAX_VALUE - this.desiredMaxFileSize)) {
074      this.desiredMaxFileSize = Long.MAX_VALUE;
075    } else {
076      this.desiredMaxFileSize += jitterValue;
077    }
078  }
079
080  @Override
081  protected boolean shouldSplit() {
082    if (!canSplit()) {
083      return false;
084    }
085    return isExceedSize(desiredMaxFileSize);
086  }
087
088  long getDesiredMaxFileSize() {
089    return desiredMaxFileSize;
090  }
091
092  @InterfaceAudience.Private
093  public boolean positiveJitterRate() {
094    return this.jitterRate > 0;
095  }
096
097  /** Returns true if region size exceed the sizeToCheck */
098  protected final boolean isExceedSize(long sizeToCheck) {
099    if (overallHRegionFiles) {
100      long sumSize = 0;
101      for (HStore store : region.getStores()) {
102        sumSize += store.getSize();
103      }
104      if (sumSize > sizeToCheck) {
105        LOG.debug("Should split because region size is big enough " + "sumSize={}, sizeToCheck={}",
106          StringUtils.humanSize(sumSize), StringUtils.humanSize(sizeToCheck));
107        return true;
108      }
109    } else {
110      for (HStore store : region.getStores()) {
111        long size = store.getSize();
112        if (size > sizeToCheck) {
113          LOG.debug("Should split because {} size={}, sizeToCheck={}", store.getColumnFamilyName(),
114            StringUtils.humanSize(size), StringUtils.humanSize(sizeToCheck));
115          return true;
116        }
117      }
118    }
119    return false;
120  }
121}