Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
public class MemorySizeUtil {

public static final String MEMSTORE_SIZE_KEY = "hbase.regionserver.global.memstore.size";
public static final String MEMSTORE_MEMORY_SIZE_KEY =
"hbase.regionserver.global.memstore.memory.size";
public static final String MEMSTORE_SIZE_OLD_KEY =
"hbase.regionserver.global.memstore.upperLimit";
public static final String MEMSTORE_SIZE_LOWER_LIMIT_KEY =
Expand Down Expand Up @@ -105,9 +107,10 @@ public static void validateRegionServerHeapMemoryAllocation(Configuration conf)
throw new RuntimeException(String.format(
"RegionServer heap memory allocation is invalid: total memory usage exceeds 100%% "
+ "(memStore + blockCache + requiredFreeHeap). "
+ "Check the following configuration values:%n" + " - %s = %.2f%n" + " - %s = %s%n"
+ " - %s = %s%n" + " - %s = %s",
MEMSTORE_SIZE_KEY, memStoreFraction, HConstants.HFILE_BLOCK_CACHE_MEMORY_SIZE_KEY,
+ "Check the following configuration values:%n" + " - %s = %s%n" + " - %s = %s%n"
+ " - %s = %s%n" + " - %s = %s%n" + " - %s = %s",
MEMSTORE_MEMORY_SIZE_KEY, conf.get(MEMSTORE_MEMORY_SIZE_KEY), MEMSTORE_SIZE_KEY,
conf.get(MEMSTORE_SIZE_KEY), HConstants.HFILE_BLOCK_CACHE_MEMORY_SIZE_KEY,
conf.get(HConstants.HFILE_BLOCK_CACHE_MEMORY_SIZE_KEY),
HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, conf.get(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY),
HBASE_REGION_SERVER_FREE_HEAP_MIN_MEMORY_SIZE_KEY,
Expand Down Expand Up @@ -151,10 +154,29 @@ public static float getRegionServerMinFreeHeapFraction(final Configuration conf)
/**
* Retrieve global memstore configured size as percentage of total heap.
*/
public static float getGlobalMemStoreHeapPercent(final Configuration c,
public static float getGlobalMemStoreHeapPercent(final Configuration conf,
final boolean logInvalid) {
// Check if an explicit memstore size is configured.
long memStoreSizeInBytes = getMemstoreSizeInBytes(conf);
if (memStoreSizeInBytes > 0) {
final MemoryUsage usage = safeGetHeapMemoryUsage();
if (usage != null) {
float memStoreSizeFraction = (float) memStoreSizeInBytes / usage.getMax();
if (memStoreSizeFraction > 0.0f && memStoreSizeFraction <= 0.8f) {
return memStoreSizeFraction;
}
}
}

if (logInvalid) {
LOG.warn(
"Setting global memstore memory size to {} bytes fails, because supplied value outside "
+ "allowed range of (0 -> 0.8]",
memStoreSizeInBytes);
}

float limit =
c.getFloat(MEMSTORE_SIZE_KEY, c.getFloat(MEMSTORE_SIZE_OLD_KEY, DEFAULT_MEMSTORE_SIZE));
conf.getFloat(MEMSTORE_SIZE_KEY, conf.getFloat(MEMSTORE_SIZE_OLD_KEY, DEFAULT_MEMSTORE_SIZE));
if (limit > 0.8f || limit <= 0.0f) {
if (logInvalid) {
LOG.warn("Setting global memstore limit to default of " + DEFAULT_MEMSTORE_SIZE
Expand Down Expand Up @@ -204,17 +226,17 @@ public static float getGlobalMemStoreHeapLowerMark(final Configuration conf,
public static Pair<Long, MemoryType> getGlobalMemStoreSize(Configuration conf) {
long offheapMSGlobal = conf.getLong(OFFHEAP_MEMSTORE_SIZE_KEY, 0);// Size in MBs
if (offheapMSGlobal > 0) {
// Off heap memstore size has not relevance when MSLAB is turned OFF. We will go with making
// this entire size split into Chunks and pooling them in MemstoreLABPoool. We dont want to
// Off heap memstore size has no relevance when MSLAB is turned OFF. We will go with making
// this entire size split into Chunks and pooling them in MemstoreLABPool. We don't want to
// create so many on demand off heap chunks. In fact when this off heap size is configured, we
// will go with 100% of this size as the pool size
if (MemStoreLAB.isEnabled(conf)) {
// We are in offheap Memstore use
long globalMemStoreLimit = (long) (offheapMSGlobal * 1024 * 1024); // Size in bytes
// We are in off heap memstore use
long globalMemStoreLimit = offheapMSGlobal * 1024 * 1024; // Size in bytes
return new Pair<>(globalMemStoreLimit, MemoryType.NON_HEAP);
} else {
// Off heap max memstore size is configured with turning off MSLAB. It makes no sense. Do a
// warn log and go with on heap memstore percentage. By default it will be 40% of Xmx
// warn log and go with on heap memstore percentage. By default, it will be 40% of Xmx
LOG.warn("There is no relevance of configuring '" + OFFHEAP_MEMSTORE_SIZE_KEY + "' when '"
+ MemStoreLAB.USEMSLAB_KEY + "' is turned off."
+ " Going with on heap global memstore size ('" + MEMSTORE_SIZE_KEY + "')");
Expand Down Expand Up @@ -300,6 +322,20 @@ public static long getOnHeapCacheSize(final Configuration conf) {
}
}

/**
* Retrieve an explicit memstore size in bytes in the configuration.
* @param conf used to read memstore configs
* @return the number of bytes to use for memstore, negative if not configured.
* @throws IllegalArgumentException if {@code MEMSTORE_MEMORY_SIZE_KEY} format is invalid
*/
public static long getMemstoreSizeInBytes(Configuration conf) {
try {
return Long.parseLong(conf.get(MEMSTORE_MEMORY_SIZE_KEY, "-1"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we support human-friendly values here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, @ndimiduk, sure, we support human-friendly values, just like in blockcache, and I add some more test cases in jira, please help take a look when free, thanks!!!

} catch (NumberFormatException e) {
return (long) conf.getStorageSize(MEMSTORE_MEMORY_SIZE_KEY, -1, StorageUnit.BYTES);
}
}

/**
* @param conf used to read config for bucket cache size.
* @return the number of bytes to use for bucket cache, negative if disabled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

import static org.apache.hadoop.hbase.HConstants.HFILE_BLOCK_CACHE_MEMORY_SIZE_KEY;
import static org.apache.hadoop.hbase.HConstants.HFILE_BLOCK_CACHE_SIZE_KEY;
import static org.apache.hadoop.hbase.io.util.MemorySizeUtil.MEMSTORE_MEMORY_SIZE_KEY;
import static org.apache.hadoop.hbase.io.util.MemorySizeUtil.MEMSTORE_SIZE_KEY;

import java.lang.management.MemoryUsage;
import java.util.ArrayList;
Expand Down Expand Up @@ -120,7 +122,7 @@ public class HeapMemoryManager {

private ResizableBlockCache toResizableBlockCache(BlockCache blockCache) {
if (blockCache instanceof CombinedBlockCache) {
return (ResizableBlockCache) ((CombinedBlockCache) blockCache).getFirstLevelCache();
return ((CombinedBlockCache) blockCache).getFirstLevelCache();
} else {
return (ResizableBlockCache) blockCache;
}
Expand All @@ -137,16 +139,20 @@ private boolean doInit(Configuration conf) {
globalMemStorePercentMaxRange =
conf.getFloat(MEMSTORE_SIZE_MAX_RANGE_KEY, globalMemStorePercent);
if (globalMemStorePercent < globalMemStorePercentMinRange) {
LOG.warn("Setting " + MEMSTORE_SIZE_MIN_RANGE_KEY + " to " + globalMemStorePercent
+ ", same value as " + MemorySizeUtil.MEMSTORE_SIZE_KEY
+ " because supplied value greater than initial memstore size value.");
LOG.warn(
"Setting {} to {} (lookup order: {} -> {}), same value as "
+ " because supplied value less than initial memstore size value.",
MEMSTORE_SIZE_MIN_RANGE_KEY, globalMemStorePercent, MEMSTORE_MEMORY_SIZE_KEY,
MEMSTORE_SIZE_KEY);
globalMemStorePercentMinRange = globalMemStorePercent;
conf.setFloat(MEMSTORE_SIZE_MIN_RANGE_KEY, globalMemStorePercentMinRange);
}
if (globalMemStorePercent > globalMemStorePercentMaxRange) {
LOG.warn("Setting " + MEMSTORE_SIZE_MAX_RANGE_KEY + " to " + globalMemStorePercent
+ ", same value as " + MemorySizeUtil.MEMSTORE_SIZE_KEY
+ " because supplied value less than initial memstore size value.");
LOG.warn(
"Setting {} to {} (lookup order: {} -> {}), same value as "
+ " because supplied value greater than initial memstore size value.",
MEMSTORE_SIZE_MAX_RANGE_KEY, globalMemStorePercent, MEMSTORE_MEMORY_SIZE_KEY,
MEMSTORE_SIZE_KEY);
globalMemStorePercentMaxRange = globalMemStorePercent;
conf.setFloat(MEMSTORE_SIZE_MAX_RANGE_KEY, globalMemStorePercentMaxRange);
}
Expand Down Expand Up @@ -376,8 +382,8 @@ private void tune() {
LOG.info("Current heap configuration from HeapMemoryTuner exceeds "
+ "the allowed heap usage. At least " + minFreeHeapFraction
+ " of the heap must remain free to ensure stable RegionServer operation. "
+ MemorySizeUtil.MEMSTORE_SIZE_KEY + " is " + memstoreSize + " and "
+ HFILE_BLOCK_CACHE_SIZE_KEY + " is " + blockCacheSize);
+ MEMSTORE_SIZE_KEY + " is " + memstoreSize + " and " + HFILE_BLOCK_CACHE_SIZE_KEY
+ " is " + blockCacheSize);
// NOTE: In the future, we might adjust values to not exceed limits,
// but for now tuning is skipped if over threshold.
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,25 @@
*/
package org.apache.hadoop.hbase.io.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.testclassification.MiscTests;
import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Category({ MiscTests.class, SmallTests.class })
@Tag(MiscTests.TAG)
@Tag(SmallTests.TAG)
public class TestMemorySizeUtil {

@ClassRule
public static final HBaseClassTestRule CLASS_RULE =
HBaseClassTestRule.forClass(TestMemorySizeUtil.class);

private Configuration conf;

@Before
@BeforeEach
public void setup() {
conf = new Configuration();
}
Expand Down Expand Up @@ -86,4 +82,37 @@ public void testGetRegionServerMinFreeHeapFraction() {
minFreeHeapFraction = MemorySizeUtil.getRegionServerMinFreeHeapFraction(conf);
assertEquals(0.0f, minFreeHeapFraction, 0.0f);
}

@Test
public void testGetMemstoreSizeInBytes() {
// when memstore size is not set, it should return -1
long memstoreSizeInBytes = MemorySizeUtil.getMemstoreSizeInBytes(conf);
assertEquals(-1, memstoreSizeInBytes);

long expectedMemstoreSizeInBytes = 123456L;
conf.setLong(MemorySizeUtil.MEMSTORE_MEMORY_SIZE_KEY, expectedMemstoreSizeInBytes);
memstoreSizeInBytes = MemorySizeUtil.getMemstoreSizeInBytes(conf);
assertEquals(expectedMemstoreSizeInBytes, memstoreSizeInBytes);

conf.set(MemorySizeUtil.MEMSTORE_MEMORY_SIZE_KEY, "10m");
memstoreSizeInBytes = MemorySizeUtil.getMemstoreSizeInBytes(conf);
assertEquals(10 * 1024 * 1024, memstoreSizeInBytes);

conf.set(MemorySizeUtil.MEMSTORE_MEMORY_SIZE_KEY, "2GB");
memstoreSizeInBytes = MemorySizeUtil.getMemstoreSizeInBytes(conf);
assertEquals(2L * 1024 * 1024 * 1024, memstoreSizeInBytes);
}

@Test
public void testGetGlobalMemStoreHeapPercent() {
// set memstore size to a small value
conf.setLong(MemorySizeUtil.MEMSTORE_MEMORY_SIZE_KEY, 1);
conf.setFloat(MemorySizeUtil.MEMSTORE_SIZE_KEY, 0.4f);
conf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0.5f);
assertEquals(HConstants.HBASE_CLUSTER_MINIMUM_MEMORY_THRESHOLD, 0.2f, 0.0f);
float globalMemStoreHeapPercent = MemorySizeUtil.getGlobalMemStoreHeapPercent(conf, true);
assertTrue(globalMemStoreHeapPercent > 0.0f);
assertTrue(globalMemStoreHeapPercent < 0.4f);
MemorySizeUtil.validateRegionServerHeapMemoryAllocation(conf);
}
}
Loading