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 org.apache.yetus.audience.InterfaceAudience; 021 022/** 023 * Base class of StochasticLoadBalancer's Cost Functions. 024 */ 025@InterfaceAudience.Private 026abstract class CostFunction { 027 028 public static final double COST_EPSILON = 0.0001; 029 030 private float multiplier = 0; 031 032 protected BalancerClusterState cluster; 033 034 boolean isNeeded() { 035 return true; 036 } 037 038 float getMultiplier() { 039 return multiplier; 040 } 041 042 void setMultiplier(float m) { 043 this.multiplier = m; 044 } 045 046 /** 047 * Called once per LB invocation to give the cost function to initialize it's state, and perform 048 * any costly calculation. 049 */ 050 void prepare(BalancerClusterState cluster) { 051 this.cluster = cluster; 052 } 053 054 /** 055 * Called once per cluster Action to give the cost function an opportunity to update it's state. 056 * postAction() is always called at least once before cost() is called with the cluster that this 057 * action is performed on. 058 */ 059 void postAction(BalanceAction action) { 060 switch (action.getType()) { 061 case NULL: 062 break; 063 case ASSIGN_REGION: 064 AssignRegionAction ar = (AssignRegionAction) action; 065 regionMoved(ar.getRegion(), -1, ar.getServer()); 066 break; 067 case MOVE_REGION: 068 MoveRegionAction mra = (MoveRegionAction) action; 069 regionMoved(mra.getRegion(), mra.getFromServer(), mra.getToServer()); 070 break; 071 case SWAP_REGIONS: 072 SwapRegionsAction a = (SwapRegionsAction) action; 073 regionMoved(a.getFromRegion(), a.getFromServer(), a.getToServer()); 074 regionMoved(a.getToRegion(), a.getToServer(), a.getFromServer()); 075 break; 076 default: 077 throw new RuntimeException("Uknown action:" + action.getType()); 078 } 079 } 080 081 protected void regionMoved(int region, int oldServer, int newServer) { 082 } 083 084 protected abstract double cost(); 085 086 /** 087 * Add the cost of this cost function to the weight of the candidate generator that is optimized 088 * for this cost function. By default it is the RandomCandiateGenerator for a cost function. 089 * Called once per init or after postAction. 090 * @param weights the weights for every generator. 091 */ 092 public void updateWeight(double[] weights) { 093 weights[StochasticLoadBalancer.GeneratorType.RANDOM.ordinal()] += cost(); 094 } 095 096 /** 097 * Scale the value between 0 and 1. 098 * @param min Min value 099 * @param max The Max value 100 * @param value The value to be scaled. 101 * @return The scaled value. 102 */ 103 protected static double scale(double min, double max, double value) { 104 if ( 105 max <= min || value <= min || Math.abs(max - min) <= COST_EPSILON 106 || Math.abs(value - min) <= COST_EPSILON 107 ) { 108 return 0; 109 } 110 if (max <= min || Math.abs(max - min) <= COST_EPSILON) { 111 return 0; 112 } 113 114 return Math.max(0d, Math.min(1d, (value - min) / (max - min))); 115 } 116}