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.regionserver;
019
020import static org.apache.hadoop.hbase.HConstants.HFILE_BLOCK_CACHE_MEMORY_SIZE_KEY;
021import static org.apache.hadoop.hbase.HConstants.HFILE_BLOCK_CACHE_SIZE_KEY;
022import static org.apache.hadoop.hbase.io.util.MemorySizeUtil.MEMSTORE_MEMORY_SIZE_KEY;
023import static org.apache.hadoop.hbase.io.util.MemorySizeUtil.MEMSTORE_SIZE_KEY;
024
025import java.lang.management.MemoryUsage;
026import java.util.ArrayList;
027import java.util.List;
028import java.util.concurrent.atomic.AtomicLong;
029import org.apache.hadoop.conf.Configuration;
030import org.apache.hadoop.hbase.ChoreService;
031import org.apache.hadoop.hbase.HConstants;
032import org.apache.hadoop.hbase.ScheduledChore;
033import org.apache.hadoop.hbase.Server;
034import org.apache.hadoop.hbase.io.hfile.BlockCache;
035import org.apache.hadoop.hbase.io.hfile.CombinedBlockCache;
036import org.apache.hadoop.hbase.io.hfile.ResizableBlockCache;
037import org.apache.hadoop.hbase.io.util.MemorySizeUtil;
038import org.apache.hadoop.util.ReflectionUtils;
039import org.apache.yetus.audience.InterfaceAudience;
040import org.slf4j.Logger;
041import org.slf4j.LoggerFactory;
042
043/**
044 * Manages tuning of Heap memory using <code>HeapMemoryTuner</code>. Most part of the heap memory is
045 * split between Memstores and BlockCache. This manager helps in tuning sizes of both these
046 * dynamically, as per the R/W load on the servers.
047 */
048@InterfaceAudience.Private
049public class HeapMemoryManager {
050  private static final Logger LOG = LoggerFactory.getLogger(HeapMemoryManager.class);
051  private static final int CONVERT_TO_PERCENTAGE = 100;
052
053  public static final String BLOCK_CACHE_SIZE_MAX_RANGE_KEY = "hfile.block.cache.size.max.range";
054  public static final String BLOCK_CACHE_SIZE_MIN_RANGE_KEY = "hfile.block.cache.size.min.range";
055  public static final String MEMSTORE_SIZE_MAX_RANGE_KEY =
056    "hbase.regionserver.global.memstore.size.max.range";
057  public static final String MEMSTORE_SIZE_MIN_RANGE_KEY =
058    "hbase.regionserver.global.memstore.size.min.range";
059  public static final String HBASE_RS_HEAP_MEMORY_TUNER_PERIOD =
060    "hbase.regionserver.heapmemory.tuner.period";
061  public static final int HBASE_RS_HEAP_MEMORY_TUNER_DEFAULT_PERIOD = 60 * 1000;
062  public static final String HBASE_RS_HEAP_MEMORY_TUNER_CLASS =
063    "hbase.regionserver.heapmemory.tuner.class";
064
065  public static final float HEAP_OCCUPANCY_ERROR_VALUE = -0.0f;
066
067  private float globalMemStorePercent;
068  private float globalMemStorePercentMinRange;
069  private float globalMemStorePercentMaxRange;
070
071  private float blockCachePercent;
072  private float blockCachePercentMinRange;
073  private float blockCachePercentMaxRange;
074
075  private float heapOccupancyPercent;
076
077  private final ResizableBlockCache blockCache;
078  // TODO : remove this and mark regionServerAccounting as the observer directly
079  private final FlushRequester memStoreFlusher;
080  private final Server server;
081  private final RegionServerAccounting regionServerAccounting;
082
083  private HeapMemoryTunerChore heapMemTunerChore = null;
084  private final boolean tunerOn;
085  private final int defaultChorePeriod;
086  private final float heapOccupancyLowWatermark;
087  private final float minFreeHeapFraction;
088
089  private final long maxHeapSize;
090  {
091    // note that this initialization still isn't threadsafe, because updating a long isn't atomic.
092    long tempMaxHeap = -1L;
093    try {
094      final MemoryUsage usage = MemorySizeUtil.safeGetHeapMemoryUsage();
095      if (usage != null) {
096        tempMaxHeap = usage.getMax();
097      }
098    } finally {
099      maxHeapSize = tempMaxHeap;
100    }
101  }
102
103  private MetricsHeapMemoryManager metricsHeapMemoryManager;
104
105  private List<HeapMemoryTuneObserver> tuneObservers = new ArrayList<>();
106
107  HeapMemoryManager(BlockCache blockCache, FlushRequester memStoreFlusher, Server server,
108    RegionServerAccounting regionServerAccounting) {
109    Configuration conf = server.getConfiguration();
110    this.blockCache = toResizableBlockCache(blockCache);
111    this.memStoreFlusher = memStoreFlusher;
112    this.server = server;
113    this.regionServerAccounting = regionServerAccounting;
114    this.minFreeHeapFraction = MemorySizeUtil.getRegionServerMinFreeHeapFraction(conf);
115    this.tunerOn = doInit(conf);
116    this.defaultChorePeriod =
117      conf.getInt(HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, HBASE_RS_HEAP_MEMORY_TUNER_DEFAULT_PERIOD);
118    this.heapOccupancyLowWatermark = conf.getFloat(HConstants.HEAP_OCCUPANCY_LOW_WATERMARK_KEY,
119      HConstants.DEFAULT_HEAP_OCCUPANCY_LOW_WATERMARK);
120    metricsHeapMemoryManager = new MetricsHeapMemoryManager();
121  }
122
123  private ResizableBlockCache toResizableBlockCache(BlockCache blockCache) {
124    if (blockCache instanceof CombinedBlockCache) {
125      return ((CombinedBlockCache) blockCache).getFirstLevelCache();
126    } else {
127      return (ResizableBlockCache) blockCache;
128    }
129  }
130
131  private boolean doInit(Configuration conf) {
132    boolean tuningEnabled = true;
133    globalMemStorePercent = MemorySizeUtil.getGlobalMemStoreHeapPercent(conf, false);
134    blockCachePercent = MemorySizeUtil.getBlockCacheHeapPercent(conf);
135    MemorySizeUtil.validateRegionServerHeapMemoryAllocation(conf);
136    // Initialize max and min range for memstore heap space
137    globalMemStorePercentMinRange =
138      conf.getFloat(MEMSTORE_SIZE_MIN_RANGE_KEY, globalMemStorePercent);
139    globalMemStorePercentMaxRange =
140      conf.getFloat(MEMSTORE_SIZE_MAX_RANGE_KEY, globalMemStorePercent);
141    if (globalMemStorePercent < globalMemStorePercentMinRange) {
142      LOG.warn(
143        "Setting {} to {} (lookup order: {} -> {}), "
144          + "because supplied value greater than initial memstore size.",
145        MEMSTORE_SIZE_MIN_RANGE_KEY, globalMemStorePercent, MEMSTORE_MEMORY_SIZE_KEY,
146        MEMSTORE_SIZE_KEY);
147      globalMemStorePercentMinRange = globalMemStorePercent;
148      conf.setFloat(MEMSTORE_SIZE_MIN_RANGE_KEY, globalMemStorePercentMinRange);
149    }
150    if (globalMemStorePercent > globalMemStorePercentMaxRange) {
151      LOG.warn(
152        "Setting {} to {} (lookup order: {} -> {}), "
153          + "because supplied value less than initial memstore size.",
154        MEMSTORE_SIZE_MAX_RANGE_KEY, globalMemStorePercent, MEMSTORE_MEMORY_SIZE_KEY,
155        MEMSTORE_SIZE_KEY);
156      globalMemStorePercentMaxRange = globalMemStorePercent;
157      conf.setFloat(MEMSTORE_SIZE_MAX_RANGE_KEY, globalMemStorePercentMaxRange);
158    }
159    if (
160      globalMemStorePercent == globalMemStorePercentMinRange
161        && globalMemStorePercent == globalMemStorePercentMaxRange
162    ) {
163      tuningEnabled = false;
164    }
165    // Initialize max and min range for block cache
166    blockCachePercentMinRange = conf.getFloat(BLOCK_CACHE_SIZE_MIN_RANGE_KEY, blockCachePercent);
167    blockCachePercentMaxRange = conf.getFloat(BLOCK_CACHE_SIZE_MAX_RANGE_KEY, blockCachePercent);
168    if (blockCachePercent < blockCachePercentMinRange) {
169      LOG.warn(
170        "Setting {} to {} (lookup order: {} -> {}), "
171          + "because supplied value greater than initial block cache size.",
172        BLOCK_CACHE_SIZE_MIN_RANGE_KEY, blockCachePercent, HFILE_BLOCK_CACHE_MEMORY_SIZE_KEY,
173        HFILE_BLOCK_CACHE_SIZE_KEY);
174      blockCachePercentMinRange = blockCachePercent;
175      conf.setFloat(BLOCK_CACHE_SIZE_MIN_RANGE_KEY, blockCachePercentMinRange);
176    }
177    if (blockCachePercent > blockCachePercentMaxRange) {
178      LOG.warn(
179        "Setting {} to {} (lookup order: {} -> {}), "
180          + "because supplied value less than initial block cache size.",
181        BLOCK_CACHE_SIZE_MAX_RANGE_KEY, blockCachePercent, HFILE_BLOCK_CACHE_MEMORY_SIZE_KEY,
182        HFILE_BLOCK_CACHE_SIZE_KEY);
183      blockCachePercentMaxRange = blockCachePercent;
184      conf.setFloat(BLOCK_CACHE_SIZE_MAX_RANGE_KEY, blockCachePercentMaxRange);
185    }
186    if (
187      tuningEnabled && blockCachePercent == blockCachePercentMinRange
188        && blockCachePercent == blockCachePercentMaxRange
189    ) {
190      tuningEnabled = false;
191    }
192
193    checkHeapMemoryLimits(MEMSTORE_SIZE_MAX_RANGE_KEY, globalMemStorePercentMaxRange,
194      BLOCK_CACHE_SIZE_MIN_RANGE_KEY, blockCachePercentMinRange);
195    checkHeapMemoryLimits(MEMSTORE_SIZE_MIN_RANGE_KEY, globalMemStorePercentMinRange,
196      BLOCK_CACHE_SIZE_MAX_RANGE_KEY, blockCachePercentMaxRange);
197
198    return tuningEnabled;
199  }
200
201  public void start(ChoreService service) {
202    LOG.info("Starting, tuneOn={}", this.tunerOn);
203    this.heapMemTunerChore = new HeapMemoryTunerChore();
204    service.scheduleChore(heapMemTunerChore);
205    if (tunerOn) {
206      // Register HeapMemoryTuner as a memstore flush listener
207      memStoreFlusher.registerFlushRequestListener(heapMemTunerChore);
208    }
209  }
210
211  public void stop() {
212    // The thread is Daemon. Just interrupting the ongoing process.
213    LOG.info("Stopping");
214    this.heapMemTunerChore.shutdown(true);
215  }
216
217  public void registerTuneObserver(HeapMemoryTuneObserver observer) {
218    this.tuneObservers.add(observer);
219  }
220
221  // Used by the test cases.
222  boolean isTunerOn() {
223    return this.tunerOn;
224  }
225
226  /** Returns heap occupancy percentage, 0 &lt;= n &lt;= 1. or -0.0 for error asking JVM */
227  public float getHeapOccupancyPercent() {
228    return this.heapOccupancyPercent == Float.MAX_VALUE
229      ? HEAP_OCCUPANCY_ERROR_VALUE
230      : this.heapOccupancyPercent;
231  }
232
233  private boolean isHeapMemoryUsageExceedingLimit(float memStoreFraction,
234    float blockCacheFraction) {
235    // Use integer percentage to avoid subtle float precision issues and ensure consistent
236    // comparison. This also maintains backward compatibility with previous logic relying on int
237    // truncation.
238    int memStorePercent = (int) (memStoreFraction * CONVERT_TO_PERCENTAGE);
239    int blockCachePercent = (int) (blockCacheFraction * CONVERT_TO_PERCENTAGE);
240    int minFreeHeapPercent = (int) (this.minFreeHeapFraction * CONVERT_TO_PERCENTAGE);
241
242    return memStorePercent + blockCachePercent + minFreeHeapPercent > CONVERT_TO_PERCENTAGE;
243  }
244
245  private void checkHeapMemoryLimits(String memStoreConfKey, float memStoreFraction,
246    String blockCacheConfKey, float blockCacheFraction) {
247    if (isHeapMemoryUsageExceedingLimit(memStoreFraction, blockCacheFraction)) {
248      throw new RuntimeException(String.format(
249        "Current heap configuration for MemStore and BlockCache exceeds the allowed heap usage. "
250          + "At least %.2f of the heap must remain free to ensure stable RegionServer operation. "
251          + "Please check the settings for %s and %s in your configuration. "
252          + "%s is %.2f and %s is %.2f",
253        minFreeHeapFraction, memStoreConfKey, blockCacheConfKey, memStoreConfKey, memStoreFraction,
254        blockCacheConfKey, blockCacheFraction));
255    }
256  }
257
258  private class HeapMemoryTunerChore extends ScheduledChore implements FlushRequestListener {
259    private HeapMemoryTuner heapMemTuner;
260    private AtomicLong blockedFlushCount = new AtomicLong();
261    private AtomicLong unblockedFlushCount = new AtomicLong();
262    private long evictCount = 0L;
263    private long cacheMissCount = 0L;
264    private TunerContext tunerContext = new TunerContext();
265    private boolean alarming = false;
266
267    public HeapMemoryTunerChore() {
268      super(server.getServerName() + "-HeapMemoryTunerChore", server, defaultChorePeriod);
269      Class<? extends HeapMemoryTuner> tunerKlass = server.getConfiguration().getClass(
270        HBASE_RS_HEAP_MEMORY_TUNER_CLASS, DefaultHeapMemoryTuner.class, HeapMemoryTuner.class);
271      heapMemTuner = ReflectionUtils.newInstance(tunerKlass, server.getConfiguration());
272      tunerContext.setOffheapMemStore(regionServerAccounting.isOffheap());
273    }
274
275    @Override
276    protected void chore() {
277      // Sample heap occupancy
278      final MemoryUsage usage = MemorySizeUtil.safeGetHeapMemoryUsage();
279      if (usage != null) {
280        heapOccupancyPercent = (float) usage.getUsed() / (float) usage.getCommitted();
281      } else {
282        // previously, an exception would have meant death for the tuning chore
283        // so switch to alarming so that we similarly stop tuning until we get
284        // heap usage information again.
285        heapOccupancyPercent = Float.MAX_VALUE;
286      }
287      // If we are above the heap occupancy alarm low watermark, switch to short
288      // sleeps for close monitoring. Stop autotuning, we are in a danger zone.
289      if (heapOccupancyPercent >= heapOccupancyLowWatermark) {
290        if (!alarming) {
291          LOG.warn("heapOccupancyPercent " + heapOccupancyPercent
292            + " is above heap occupancy alarm watermark (" + heapOccupancyLowWatermark + ")");
293          alarming = true;
294        }
295        metricsHeapMemoryManager.increaseAboveHeapOccupancyLowWatermarkCounter();
296        triggerNow();
297        try {
298          // Need to sleep ourselves since we've told the chore's sleeper
299          // to skip the next sleep cycle.
300          Thread.sleep(1000);
301        } catch (InterruptedException e) {
302          // Interrupted, propagate
303          Thread.currentThread().interrupt();
304        }
305      } else {
306        if (alarming) {
307          LOG.info("heapOccupancyPercent " + heapOccupancyPercent
308            + " is now below the heap occupancy alarm watermark (" + heapOccupancyLowWatermark
309            + ")");
310          alarming = false;
311        }
312      }
313      // Autotune if tuning is enabled and allowed
314      if (tunerOn && !alarming) {
315        tune();
316      }
317    }
318
319    private void tune() {
320      // TODO check if we can increase the memory boundaries
321      // while remaining in the limits
322      long curEvictCount;
323      long curCacheMisCount;
324      long blockedFlushCnt;
325      long unblockedFlushCnt;
326      curEvictCount = blockCache.getStats().getEvictedCount();
327      tunerContext.setEvictCount(curEvictCount - evictCount);
328      evictCount = curEvictCount;
329      curCacheMisCount = blockCache.getStats().getMissCachingCount();
330      tunerContext.setCacheMissCount(curCacheMisCount - cacheMissCount);
331      cacheMissCount = curCacheMisCount;
332      blockedFlushCnt = blockedFlushCount.getAndSet(0);
333      tunerContext.setBlockedFlushCount(blockedFlushCnt);
334      metricsHeapMemoryManager.updateBlockedFlushCount(blockedFlushCnt);
335      unblockedFlushCnt = unblockedFlushCount.getAndSet(0);
336      tunerContext.setUnblockedFlushCount(unblockedFlushCnt);
337      metricsHeapMemoryManager.updateUnblockedFlushCount(unblockedFlushCnt);
338      tunerContext.setCurBlockCacheUsed((float) blockCache.getCurrentSize() / maxHeapSize);
339      metricsHeapMemoryManager.setCurBlockCacheSizeGauge(blockCache.getCurrentSize());
340      long globalMemstoreDataSize = regionServerAccounting.getGlobalMemStoreDataSize();
341      long globalMemstoreHeapSize = regionServerAccounting.getGlobalMemStoreHeapSize();
342      long globalMemStoreOffHeapSize = regionServerAccounting.getGlobalMemStoreOffHeapSize();
343      tunerContext.setCurMemStoreUsed((float) globalMemstoreHeapSize / maxHeapSize);
344      metricsHeapMemoryManager.setCurMemStoreSizeGauge(globalMemstoreDataSize);
345      metricsHeapMemoryManager.setCurMemStoreOnHeapSizeGauge(globalMemstoreHeapSize);
346      metricsHeapMemoryManager.setCurMemStoreOffHeapSizeGauge(globalMemStoreOffHeapSize);
347      tunerContext.setCurBlockCacheSize(blockCachePercent);
348      tunerContext.setCurMemStoreSize(globalMemStorePercent);
349      TunerResult result = null;
350      try {
351        result = this.heapMemTuner.tune(tunerContext);
352      } catch (Throwable t) {
353        LOG.error("Exception thrown from the HeapMemoryTuner implementation", t);
354      }
355      if (result != null && result.needsTuning()) {
356        float memstoreSize = result.getMemStoreSize();
357        float blockCacheSize = result.getBlockCacheSize();
358        LOG.debug("From HeapMemoryTuner new memstoreSize: " + memstoreSize
359          + ". new blockCacheSize: " + blockCacheSize);
360        if (memstoreSize < globalMemStorePercentMinRange) {
361          LOG.info("New memstoreSize from HeapMemoryTuner " + memstoreSize + " is below min level "
362            + globalMemStorePercentMinRange + ". Resetting memstoreSize to min size");
363          memstoreSize = globalMemStorePercentMinRange;
364        } else if (memstoreSize > globalMemStorePercentMaxRange) {
365          LOG.info("New memstoreSize from HeapMemoryTuner " + memstoreSize + " is above max level "
366            + globalMemStorePercentMaxRange + ". Resetting memstoreSize to max size");
367          memstoreSize = globalMemStorePercentMaxRange;
368        }
369        if (blockCacheSize < blockCachePercentMinRange) {
370          LOG.info(
371            "New blockCacheSize from HeapMemoryTuner " + blockCacheSize + " is below min level "
372              + blockCachePercentMinRange + ". Resetting blockCacheSize to min size");
373          blockCacheSize = blockCachePercentMinRange;
374        } else if (blockCacheSize > blockCachePercentMaxRange) {
375          LOG.info(
376            "New blockCacheSize from HeapMemoryTuner " + blockCacheSize + " is above max level "
377              + blockCachePercentMaxRange + ". Resetting blockCacheSize to min size");
378          blockCacheSize = blockCachePercentMaxRange;
379        }
380
381        if (isHeapMemoryUsageExceedingLimit(memstoreSize, blockCacheSize)) {
382          LOG.info("Current heap configuration from HeapMemoryTuner exceeds "
383            + "the allowed heap usage. At least " + minFreeHeapFraction
384            + " of the heap must remain free to ensure stable RegionServer operation. "
385            + MEMSTORE_SIZE_KEY + " is " + memstoreSize + " and " + HFILE_BLOCK_CACHE_SIZE_KEY
386            + " is " + blockCacheSize);
387          // NOTE: In the future, we might adjust values to not exceed limits,
388          // but for now tuning is skipped if over threshold.
389        } else {
390          int memStoreDeltaSize =
391            (int) ((memstoreSize - globalMemStorePercent) * CONVERT_TO_PERCENTAGE);
392          int blockCacheDeltaSize =
393            (int) ((blockCacheSize - blockCachePercent) * CONVERT_TO_PERCENTAGE);
394          metricsHeapMemoryManager.updateMemStoreDeltaSizeHistogram(memStoreDeltaSize);
395          metricsHeapMemoryManager.updateBlockCacheDeltaSizeHistogram(blockCacheDeltaSize);
396          long newBlockCacheSize = (long) (maxHeapSize * blockCacheSize);
397          // we could have got an increase or decrease in size for the offheap memstore
398          // also if the flush had happened due to heap overhead. In that case it is ok
399          // to adjust the onheap memstore limit configs
400          long newMemstoreSize = (long) (maxHeapSize * memstoreSize);
401          LOG.info("Setting block cache heap size to " + newBlockCacheSize
402            + " and memstore heap size to " + newMemstoreSize);
403          blockCachePercent = blockCacheSize;
404          blockCache.setMaxSize(newBlockCacheSize);
405          globalMemStorePercent = memstoreSize;
406          // Internally sets it to RegionServerAccounting
407          // TODO : Set directly on RSAccounting??
408          memStoreFlusher.setGlobalMemStoreLimit(newMemstoreSize);
409          for (HeapMemoryTuneObserver observer : tuneObservers) {
410            // Risky.. If this newMemstoreSize decreases we reduce the count in offheap chunk pool
411            observer.onHeapMemoryTune(newMemstoreSize, newBlockCacheSize);
412          }
413        }
414      } else {
415        metricsHeapMemoryManager.increaseTunerDoNothingCounter();
416        if (LOG.isDebugEnabled()) {
417          LOG.debug("No changes made by HeapMemoryTuner.");
418        }
419      }
420    }
421
422    @Override
423    public void flushRequested(FlushType type, Region region) {
424      switch (type) {
425        case ABOVE_ONHEAP_HIGHER_MARK:
426          blockedFlushCount.incrementAndGet();
427          break;
428        case ABOVE_ONHEAP_LOWER_MARK:
429          unblockedFlushCount.incrementAndGet();
430          break;
431        // Removed the counting of the offheap related flushes (after reviews). Will add later if
432        // needed
433        default:
434          // In case of any other flush don't do any action.
435          break;
436      }
437    }
438  }
439
440  /**
441   * POJO to pass all the relevant information required to do the heap memory tuning. It holds the
442   * flush counts and block cache evictions happened within the interval. Also holds the current
443   * heap percentage allocated for memstore and block cache.
444   */
445  public static final class TunerContext {
446    private long blockedFlushCount;
447    private long unblockedFlushCount;
448    private long evictCount;
449    private long cacheMissCount;
450    private float curBlockCacheUsed;
451    private float curMemStoreUsed;
452    private float curMemStoreSize;
453    private float curBlockCacheSize;
454    private boolean offheapMemstore;
455
456    public long getBlockedFlushCount() {
457      return blockedFlushCount;
458    }
459
460    public void setBlockedFlushCount(long blockedFlushCount) {
461      this.blockedFlushCount = blockedFlushCount;
462    }
463
464    public long getUnblockedFlushCount() {
465      return unblockedFlushCount;
466    }
467
468    public void setUnblockedFlushCount(long unblockedFlushCount) {
469      this.unblockedFlushCount = unblockedFlushCount;
470    }
471
472    public long getEvictCount() {
473      return evictCount;
474    }
475
476    public void setEvictCount(long evictCount) {
477      this.evictCount = evictCount;
478    }
479
480    public float getCurMemStoreSize() {
481      return curMemStoreSize;
482    }
483
484    public void setCurMemStoreSize(float curMemStoreSize) {
485      this.curMemStoreSize = curMemStoreSize;
486    }
487
488    public float getCurBlockCacheSize() {
489      return curBlockCacheSize;
490    }
491
492    public void setCurBlockCacheSize(float curBlockCacheSize) {
493      this.curBlockCacheSize = curBlockCacheSize;
494    }
495
496    public long getCacheMissCount() {
497      return cacheMissCount;
498    }
499
500    public void setCacheMissCount(long cacheMissCount) {
501      this.cacheMissCount = cacheMissCount;
502    }
503
504    public float getCurBlockCacheUsed() {
505      return curBlockCacheUsed;
506    }
507
508    public void setCurBlockCacheUsed(float curBlockCacheUsed) {
509      this.curBlockCacheUsed = curBlockCacheUsed;
510    }
511
512    public float getCurMemStoreUsed() {
513      return curMemStoreUsed;
514    }
515
516    public void setCurMemStoreUsed(float d) {
517      this.curMemStoreUsed = d;
518    }
519
520    public void setOffheapMemStore(boolean offheapMemstore) {
521      this.offheapMemstore = offheapMemstore;
522    }
523
524    public boolean isOffheapMemStore() {
525      return this.offheapMemstore;
526    }
527  }
528
529  /**
530   * POJO which holds the result of memory tuning done by HeapMemoryTuner implementation. It
531   * includes the new heap percentage for memstore and block cache.
532   */
533  public static final class TunerResult {
534    private float memstoreSize;
535    private float blockCacheSize;
536    private final boolean needsTuning;
537
538    public TunerResult(boolean needsTuning) {
539      this.needsTuning = needsTuning;
540    }
541
542    public float getMemStoreSize() {
543      return memstoreSize;
544    }
545
546    public void setMemStoreSize(float memstoreSize) {
547      this.memstoreSize = memstoreSize;
548    }
549
550    public float getBlockCacheSize() {
551      return blockCacheSize;
552    }
553
554    public void setBlockCacheSize(float blockCacheSize) {
555      this.blockCacheSize = blockCacheSize;
556    }
557
558    public boolean needsTuning() {
559      return needsTuning;
560    }
561  }
562
563  /**
564   * Every class that wants to observe heap memory tune actions must implement this interface.
565   */
566  public static interface HeapMemoryTuneObserver {
567
568    /**
569     * This method would be called by HeapMemoryManger when a heap memory tune action took place.
570     * @param newMemstoreSize   The newly calculated global memstore size
571     * @param newBlockCacheSize The newly calculated global blockcache size
572     */
573    void onHeapMemoryTune(long newMemstoreSize, long newBlockCacheSize);
574  }
575}