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.master.balancer;
019
020import java.util.Arrays;
021import java.util.List;
022import java.util.Set;
023import java.util.stream.Collectors;
024import org.apache.hadoop.conf.Configuration;
025import org.apache.hadoop.hbase.HBaseInterfaceAudience;
026import org.apache.hadoop.hbase.client.RegionInfo;
027import org.apache.hadoop.hbase.master.RegionPlan;
028import org.apache.yetus.audience.InterfaceAudience;
029import org.apache.yetus.audience.InterfaceStability;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
034@InterfaceStability.Evolving
035public abstract class RegionPlanConditional {
036  private static final Logger LOG = LoggerFactory.getLogger(RegionPlanConditional.class);
037  private BalancerClusterState cluster;
038
039  RegionPlanConditional(Configuration conf, BalancerClusterState cluster) {
040    this.cluster = cluster;
041  }
042
043  public enum ValidationLevel {
044    /**
045     * Just check the server.
046     */
047    SERVER,
048    /**
049     * Check the server and the host.
050     */
051    SERVER_HOST,
052    /**
053     * Check the server, host, and rack.
054     */
055    SERVER_HOST_RACK
056  }
057
058  void setClusterState(BalancerClusterState cluster) {
059    this.cluster = cluster;
060  }
061
062  /**
063   * Returns a {@link ValidationLevel} that is appropriate for this conditional.
064   * @return the validation level
065   */
066  abstract ValidationLevel getValidationLevel();
067
068  /**
069   * Get the candidate generator(s) for this conditional. This can be useful to provide the balancer
070   * with hints that will appease your conditional. Your conditionals will be triggered in order.
071   * @return the candidate generator for this conditional
072   */
073  abstract List<RegionPlanConditionalCandidateGenerator> getCandidateGenerators();
074
075  /**
076   * Check if the conditional is violated by the given region plan.
077   * @param regionPlan the region plan to check
078   * @return true if the conditional is violated
079   */
080  boolean isViolating(RegionPlan regionPlan) {
081    if (regionPlan == null) {
082      return false;
083    }
084    int destinationServerIdx = cluster.serversToIndex.get(regionPlan.getDestination().getAddress());
085
086    // Check Server
087    int[] destinationRegionIndices = cluster.regionsPerServer[destinationServerIdx];
088    Set<RegionInfo> serverRegions = Arrays.stream(cluster.regionsPerServer[destinationServerIdx])
089      .mapToObj(idx -> cluster.regions[idx]).collect(Collectors.toSet());
090    for (int regionIdx : destinationRegionIndices) {
091      serverRegions.add(cluster.regions[regionIdx]);
092    }
093    if (isViolatingServer(regionPlan, serverRegions)) {
094      return true;
095    }
096
097    if (getValidationLevel() == ValidationLevel.SERVER) {
098      return false;
099    }
100
101    // Check Host
102    int hostIdx = cluster.serverIndexToHostIndex[destinationServerIdx];
103    Set<RegionInfo> hostRegions = Arrays.stream(cluster.regionsPerHost[hostIdx])
104      .mapToObj(idx -> cluster.regions[idx]).collect(Collectors.toSet());
105    if (isViolatingHost(regionPlan, hostRegions)) {
106      return true;
107    }
108
109    if (getValidationLevel() == ValidationLevel.SERVER_HOST) {
110      return false;
111    }
112
113    // Check Rack
114    int rackIdx = cluster.serverIndexToRackIndex[destinationServerIdx];
115    Set<RegionInfo> rackRegions = Arrays.stream(cluster.regionsPerRack[rackIdx])
116      .mapToObj(idx -> cluster.regions[idx]).collect(Collectors.toSet());
117    if (isViolatingRack(regionPlan, rackRegions)) {
118      return true;
119    }
120
121    return false;
122  }
123
124  abstract boolean isViolatingServer(RegionPlan regionPlan, Set<RegionInfo> destinationRegions);
125
126  boolean isViolatingHost(RegionPlan regionPlan, Set<RegionInfo> destinationRegions) {
127    return false;
128  }
129
130  boolean isViolatingRack(RegionPlan regionPlan, Set<RegionInfo> destinationRegions) {
131    return false;
132  }
133}