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