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.regionserver;
020
021import java.lang.management.MemoryType;
022import java.util.concurrent.atomic.LongAdder;
023
024import org.apache.hadoop.conf.Configuration;
025import org.apache.yetus.audience.InterfaceAudience;
026import org.apache.hadoop.hbase.io.util.MemorySizeUtil;
027import org.apache.hadoop.hbase.util.Pair;
028
029/**
030 * RegionServerAccounting keeps record of some basic real time information about
031 * the Region Server. Currently, it keeps record the global memstore size and global memstore
032 * on-heap and off-heap overhead. It also tracks the replay edits per region.
033 */
034@InterfaceAudience.Private
035public class RegionServerAccounting {
036  // memstore data size
037  private final LongAdder globalMemStoreDataSize = new LongAdder();
038  // memstore heap size.
039  private final LongAdder globalMemStoreHeapSize = new LongAdder();
040  // memstore off-heap size.
041  private final LongAdder globalMemStoreOffHeapSize = new LongAdder();
042
043  private long globalMemStoreLimit;
044  private final float globalMemStoreLimitLowMarkPercent;
045  private long globalMemStoreLimitLowMark;
046  private final MemoryType memType;
047  private long globalOnHeapMemstoreLimit;
048  private long globalOnHeapMemstoreLimitLowMark;
049
050  public RegionServerAccounting(Configuration conf) {
051    Pair<Long, MemoryType> globalMemstoreSizePair = MemorySizeUtil.getGlobalMemStoreSize(conf);
052    this.globalMemStoreLimit = globalMemstoreSizePair.getFirst();
053    this.memType = globalMemstoreSizePair.getSecond();
054    this.globalMemStoreLimitLowMarkPercent =
055        MemorySizeUtil.getGlobalMemStoreHeapLowerMark(conf, this.memType == MemoryType.HEAP);
056    // When off heap memstore in use we configure the global off heap space for memstore as bytes
057    // not as % of max memory size. In such case, the lower water mark should be specified using the
058    // key "hbase.regionserver.global.memstore.size.lower.limit" which says % of the global upper
059    // bound and defaults to 95%. In on heap case also specifying this way is ideal. But in the past
060    // we used to take lower bound also as the % of xmx (38% as default). For backward compatibility
061    // for this deprecated config,we will fall back to read that config when new one is missing.
062    // Only for on heap case, do this fallback mechanism. For off heap it makes no sense.
063    // TODO When to get rid of the deprecated config? ie
064    // "hbase.regionserver.global.memstore.lowerLimit". Can get rid of this boolean passing then.
065    this.globalMemStoreLimitLowMark =
066        (long) (this.globalMemStoreLimit * this.globalMemStoreLimitLowMarkPercent);
067    this.globalOnHeapMemstoreLimit = MemorySizeUtil.getOnheapGlobalMemStoreSize(conf);
068    this.globalOnHeapMemstoreLimitLowMark =
069        (long) (this.globalOnHeapMemstoreLimit * this.globalMemStoreLimitLowMarkPercent);
070  }
071
072  long getGlobalMemStoreLimit() {
073    return this.globalMemStoreLimit;
074  }
075
076  long getGlobalOnHeapMemStoreLimit() {
077    return this.globalOnHeapMemstoreLimit;
078  }
079
080  // Called by the tuners.
081  void setGlobalMemStoreLimits(long newGlobalMemstoreLimit) {
082    if (this.memType == MemoryType.HEAP) {
083      this.globalMemStoreLimit = newGlobalMemstoreLimit;
084      this.globalMemStoreLimitLowMark =
085          (long) (this.globalMemStoreLimit * this.globalMemStoreLimitLowMarkPercent);
086    } else {
087      this.globalOnHeapMemstoreLimit = newGlobalMemstoreLimit;
088      this.globalOnHeapMemstoreLimitLowMark =
089          (long) (this.globalOnHeapMemstoreLimit * this.globalMemStoreLimitLowMarkPercent);
090    }
091  }
092
093  boolean isOffheap() {
094    return this.memType == MemoryType.NON_HEAP;
095  }
096
097  long getGlobalMemStoreLimitLowMark() {
098    return this.globalMemStoreLimitLowMark;
099  }
100
101  float getGlobalMemStoreLimitLowMarkPercent() {
102    return this.globalMemStoreLimitLowMarkPercent;
103  }
104
105  /**
106   * @return the global Memstore data size in the RegionServer
107   */
108  public long getGlobalMemStoreDataSize() {
109    return globalMemStoreDataSize.sum();
110  }
111
112  /**
113   * @return the global memstore heap size in the RegionServer
114   */
115  public long getGlobalMemStoreHeapSize() {
116    return this.globalMemStoreHeapSize.sum();
117  }
118
119  /**
120   * @return the global memstore heap size in the RegionServer
121   */
122  public long getGlobalMemStoreOffHeapSize() {
123    return this.globalMemStoreOffHeapSize.sum();
124  }
125
126  void incGlobalMemStoreSize(MemStoreSize mss) {
127    incGlobalMemStoreSize(mss.getDataSize(), mss.getHeapSize(), mss.getOffHeapSize());
128  }
129
130  public void incGlobalMemStoreSize(long dataSizeDelta, long heapSizeDelta, long offHeapSizeDelta) {
131    globalMemStoreDataSize.add(dataSizeDelta);
132    globalMemStoreHeapSize.add(heapSizeDelta);
133    globalMemStoreOffHeapSize.add(offHeapSizeDelta);
134  }
135
136  public void decGlobalMemStoreSize(long dataSizeDelta, long heapSizeDelta, long offHeapSizeDelta) {
137    globalMemStoreDataSize.add(-dataSizeDelta);
138    globalMemStoreHeapSize.add(-heapSizeDelta);
139    globalMemStoreOffHeapSize.add(-offHeapSizeDelta);
140  }
141
142  /**
143   * Return true if we are above the memstore high water mark
144   * @return the flushtype
145   */
146  public FlushType isAboveHighWaterMark() {
147    // for onheap memstore we check if the global memstore size and the
148    // global heap overhead is greater than the global memstore limit
149    if (memType == MemoryType.HEAP) {
150      if (getGlobalMemStoreHeapSize() >= globalMemStoreLimit) {
151        return FlushType.ABOVE_ONHEAP_HIGHER_MARK;
152      }
153    } else {
154      // If the configured memstore is offheap, check for two things
155      // 1) If the global memstore off-heap size is greater than the configured
156      // 'hbase.regionserver.offheap.global.memstore.size'
157      // 2) If the global memstore heap size is greater than the configured onheap
158      // global memstore limit 'hbase.regionserver.global.memstore.size'.
159      // We do this to avoid OOME incase of scenarios where the heap is occupied with
160      // lot of onheap references to the cells in memstore
161      if (getGlobalMemStoreOffHeapSize() >= globalMemStoreLimit) {
162        // Indicates that global memstore size is above the configured
163        // 'hbase.regionserver.offheap.global.memstore.size'
164        return FlushType.ABOVE_OFFHEAP_HIGHER_MARK;
165      } else if (getGlobalMemStoreHeapSize() >= this.globalOnHeapMemstoreLimit) {
166        // Indicates that the offheap memstore's heap overhead is greater than the
167        // configured 'hbase.regionserver.global.memstore.size'.
168        return FlushType.ABOVE_ONHEAP_HIGHER_MARK;
169      }
170    }
171    return FlushType.NORMAL;
172  }
173
174  /**
175   * Return true if we're above the low watermark
176   */
177  public FlushType isAboveLowWaterMark() {
178    // for onheap memstore we check if the global memstore size and the
179    // global heap overhead is greater than the global memstore lower mark limit
180    if (memType == MemoryType.HEAP) {
181      if (getGlobalMemStoreHeapSize() >= globalMemStoreLimitLowMark) {
182        return FlushType.ABOVE_ONHEAP_LOWER_MARK;
183      }
184    } else {
185      if (getGlobalMemStoreOffHeapSize() >= globalMemStoreLimitLowMark) {
186        // Indicates that the offheap memstore's size is greater than the global memstore
187        // lower limit
188        return FlushType.ABOVE_OFFHEAP_LOWER_MARK;
189      } else if (getGlobalMemStoreHeapSize() >= globalOnHeapMemstoreLimitLowMark) {
190        // Indicates that the offheap memstore's heap overhead is greater than the global memstore
191        // onheap lower limit
192        return FlushType.ABOVE_ONHEAP_LOWER_MARK;
193      }
194    }
195    return FlushType.NORMAL;
196  }
197
198  /**
199   * @return the flush pressure of all stores on this regionserver. The value should be greater than
200   *         or equal to 0.0, and any value greater than 1.0 means we enter the emergency state that
201   *         global memstore size already exceeds lower limit.
202   */
203  public double getFlushPressure() {
204    if (memType == MemoryType.HEAP) {
205      return (getGlobalMemStoreHeapSize()) * 1.0 / globalMemStoreLimitLowMark;
206    } else {
207      return Math.max(getGlobalMemStoreOffHeapSize() * 1.0 / globalMemStoreLimitLowMark,
208          getGlobalMemStoreHeapSize() * 1.0 / globalOnHeapMemstoreLimitLowMark);
209    }
210  }
211}