001/**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.hbase.master.normalizer;
020
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.Comparator;
024import java.util.List;
025import org.apache.hadoop.hbase.HBaseConfiguration;
026import org.apache.hadoop.hbase.HBaseIOException;
027import org.apache.hadoop.hbase.TableName;
028import org.apache.hadoop.hbase.client.RegionInfo;
029import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan.PlanType;
030import org.apache.yetus.audience.InterfaceAudience;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034/**
035 * Simple implementation of region normalizer. Logic in use:
036 * <ol>
037 * <li>Get all regions of a given table
038 * <li>Get avg size S of each region (by total size of store files reported in RegionMetrics)
039 * <li>Seek every single region one by one. If a region R0 is bigger than S * 2, it is kindly
040 * requested to split. Thereon evaluate the next region R1
041 * <li>Otherwise, if R0 + R1 is smaller than S, R0 and R1 are kindly requested to merge. Thereon
042 * evaluate the next region R2
043 * <li>Otherwise, R1 is evaluated
044 * </ol>
045 * <p>
046 * Region sizes are coarse and approximate on the order of megabytes. Additionally, "empty" regions
047 * (less than 1MB, with the previous note) are not merged away. This is by design to prevent
048 * normalization from undoing the pre-splitting of a table.
049 */
050@InterfaceAudience.Private
051public class SimpleRegionNormalizer extends AbstractRegionNormalizer {
052
053  private static final Logger LOG = LoggerFactory.getLogger(SimpleRegionNormalizer.class);
054  private int minRegionCount;
055  private static long[] skippedCount = new long[NormalizationPlan.PlanType.values().length];
056
057  public SimpleRegionNormalizer() {
058    minRegionCount = HBaseConfiguration.create().getInt("hbase.normalizer.min.region.count", 3);
059  }
060
061  @Override
062  public void planSkipped(RegionInfo hri, PlanType type) {
063    skippedCount[type.ordinal()]++;
064  }
065
066  @Override
067  public long getSkippedCount(NormalizationPlan.PlanType type) {
068    return skippedCount[type.ordinal()];
069  }
070
071  /**
072   * Comparator class that gives higher priority to region Split plan.
073   */
074  static class PlanComparator implements Comparator<NormalizationPlan> {
075    @Override
076    public int compare(NormalizationPlan plan1, NormalizationPlan plan2) {
077      boolean plan1IsSplit = plan1 instanceof SplitNormalizationPlan;
078      boolean plan2IsSplit = plan2 instanceof SplitNormalizationPlan;
079      if (plan1IsSplit && plan2IsSplit) {
080        return 0;
081      } else if (plan1IsSplit) {
082        return -1;
083      } else if (plan2IsSplit) {
084        return 1;
085      } else {
086        return 0;
087      }
088    }
089  }
090
091  private Comparator<NormalizationPlan> planComparator = new PlanComparator();
092
093  /**
094   * Computes next most "urgent" normalization action on the table. Action may be either a split, or
095   * a merge, or no action.
096   * @param table table to normalize
097   * @return normalization plan to execute
098   */
099  @Override
100  public List<NormalizationPlan> computePlanForTable(TableName table) throws HBaseIOException {
101    if (table == null || table.isSystemTable()) {
102      LOG.debug("Normalization of system table {} isn't allowed", table);
103      return null;
104    }
105    boolean splitEnabled = isSplitEnabled();
106    boolean mergeEnabled = isMergeEnabled();
107    if (!mergeEnabled && !splitEnabled) {
108      LOG.debug("Both split and merge are disabled for table: {}", table);
109      return null;
110    }
111    List<NormalizationPlan> plans = new ArrayList<>();
112    List<RegionInfo> tableRegions =
113        masterServices.getAssignmentManager().getRegionStates().getRegionsOfTable(table);
114
115    if (tableRegions == null || tableRegions.size() < minRegionCount) {
116      int nrRegions = tableRegions == null ? 0 : tableRegions.size();
117      LOG.debug("Table {} has {} regions, required min number of regions for normalizer to run is "
118          + "{}, not running normalizer",
119        table, nrRegions, minRegionCount);
120      return null;
121    }
122
123    LOG.debug("Computing normalization plan for table:  {}, number of regions: {}", table,
124      tableRegions.size());
125
126    if (splitEnabled) {
127      List<NormalizationPlan> splitPlans = getSplitNormalizationPlan(table);
128      if (splitPlans != null) {
129        plans.addAll(splitPlans);
130      }
131    }
132
133    if (mergeEnabled) {
134      List<NormalizationPlan> mergePlans = getMergeNormalizationPlan(table);
135      if (mergePlans != null) {
136        plans.addAll(mergePlans);
137      }
138    }
139    if (plans.isEmpty()) {
140      LOG.debug("No normalization needed, regions look good for table: {}", table);
141      return null;
142    }
143    Collections.sort(plans, planComparator);
144    return plans;
145  }
146}