Skip to content

Commit fd5cc19

Browse files
jhungundwchevreuil
authored andcommitted
HBASE-28535: Add a region-server wide key to enable data-tiering. (#5856)
Signed-off-by: Wellington Chevreuil <wchevreuil@apache.org>
1 parent 2427785 commit fd5cc19

4 files changed

Lines changed: 114 additions & 34 deletions

File tree

hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,11 +1156,10 @@ void freeSpace(final String why) {
11561156
long bytesFreed = 0;
11571157
// Check the list of files to determine the cold files which can be readily evicted.
11581158
Map<String, String> coldFiles = null;
1159-
try {
1160-
DataTieringManager dataTieringManager = DataTieringManager.getInstance();
1159+
1160+
DataTieringManager dataTieringManager = DataTieringManager.getInstance();
1161+
if (dataTieringManager != null) {
11611162
coldFiles = dataTieringManager.getColdFilesList();
1162-
} catch (IllegalStateException e) {
1163-
LOG.warn("Data Tiering Manager is not set. Ignore time-based block evictions.");
11641163
}
11651164
// Scan entire map putting bucket entry into appropriate bucket entry
11661165
// group
@@ -2448,16 +2447,11 @@ public Optional<Boolean> blockFitsIntoTheCache(HFileBlock block) {
24482447
@Override
24492448
public Optional<Boolean> shouldCacheFile(HFileInfo hFileInfo, Configuration conf) {
24502449
String fileName = hFileInfo.getHFileContext().getHFileName();
2451-
try {
2452-
DataTieringManager dataTieringManager = DataTieringManager.getInstance();
2453-
if (!dataTieringManager.isHotData(hFileInfo, conf)) {
2454-
LOG.debug("Data tiering is enabled for file: '{}' and it is not hot data", fileName);
2455-
return Optional.of(false);
2456-
}
2457-
} catch (IllegalStateException e) {
2458-
LOG.error("Error while getting DataTieringManager instance: {}", e.getMessage());
2450+
DataTieringManager dataTieringManager = DataTieringManager.getInstance();
2451+
if (dataTieringManager != null && !dataTieringManager.isHotData(hFileInfo, conf)) {
2452+
LOG.debug("Data tiering is enabled for file: '{}' and it is not hot data", fileName);
2453+
return Optional.of(false);
24592454
}
2460-
24612455
// if we don't have the file in fullyCachedFiles, we should cache it
24622456
return Optional.of(!fullyCachedFiles.containsKey(fileName));
24632457
}

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DataTieringManager.java

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
@InterfaceAudience.Private
4646
public class DataTieringManager {
4747
private static final Logger LOG = LoggerFactory.getLogger(DataTieringManager.class);
48+
public static final String GLOBAL_DATA_TIERING_ENABLED_KEY =
49+
"hbase.regionserver.datatiering.enable";
50+
public static final boolean DEFAULT_GLOBAL_DATA_TIERING_ENABLED = false; // disabled by default
4851
public static final String DATATIERING_KEY = "hbase.hstore.datatiering.type";
4952
public static final String DATATIERING_HOT_DATA_AGE_KEY =
5053
"hbase.hstore.datatiering.hot.age.millis";
@@ -58,28 +61,29 @@ private DataTieringManager(Map<String, HRegion> onlineRegions) {
5861
}
5962

6063
/**
61-
* Initializes the DataTieringManager instance with the provided map of online regions.
64+
* Initializes the DataTieringManager instance with the provided map of online regions, only if
65+
* the configuration "hbase.regionserver.datatiering.enable" is enabled.
66+
* @param conf Configuration object.
6267
* @param onlineRegions A map containing online regions.
68+
* @return True if the instance is instantiated successfully, false otherwise.
6369
*/
64-
public static synchronized void instantiate(Map<String, HRegion> onlineRegions) {
65-
if (instance == null) {
70+
public static synchronized boolean instantiate(Configuration conf,
71+
Map<String, HRegion> onlineRegions) {
72+
if (isDataTieringFeatureEnabled(conf) && instance == null) {
6673
instance = new DataTieringManager(onlineRegions);
6774
LOG.info("DataTieringManager instantiated successfully.");
75+
return true;
6876
} else {
6977
LOG.warn("DataTieringManager is already instantiated.");
7078
}
79+
return false;
7180
}
7281

7382
/**
7483
* Retrieves the instance of DataTieringManager.
75-
* @return The instance of DataTieringManager.
76-
* @throws IllegalStateException if DataTieringManager has not been instantiated.
84+
* @return The instance of DataTieringManager, if instantiated, null otherwise.
7785
*/
7886
public static synchronized DataTieringManager getInstance() {
79-
if (instance == null) {
80-
throw new IllegalStateException(
81-
"DataTieringManager has not been instantiated. Call instantiate() first.");
82-
}
8387
return instance;
8488
}
8589

@@ -308,4 +312,14 @@ public Map<String, String> getColdFilesList() {
308312
}
309313
return coldFiles;
310314
}
315+
316+
private static boolean isDataTieringFeatureEnabled(Configuration conf) {
317+
return conf.getBoolean(DataTieringManager.GLOBAL_DATA_TIERING_ENABLED_KEY,
318+
DataTieringManager.DEFAULT_GLOBAL_DATA_TIERING_ENABLED);
319+
}
320+
321+
// Resets the instance to null. To be used only for testing.
322+
public static void resetForTestingOnly() {
323+
instance = null;
324+
}
311325
}

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,10 @@ public HRegionServer(final Configuration conf) throws IOException {
534534
regionServerAccounting = new RegionServerAccounting(conf);
535535

536536
blockCache = BlockCacheFactory.createBlockCache(conf);
537-
DataTieringManager.instantiate(onlineRegions);
537+
// The call below, instantiates the DataTieringManager only when
538+
// the configuration "hbase.regionserver.datatiering.enable" is set to true.
539+
DataTieringManager.instantiate(conf, onlineRegions);
540+
538541
mobFileCache = new MobFileCache(conf);
539542

540543
rsSnapshotVerifier = new RSSnapshotVerifier(conf);

hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestDataTieringManager.java

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import static org.apache.hadoop.hbase.HConstants.BUCKET_CACHE_SIZE_KEY;
2121
import static org.apache.hadoop.hbase.io.hfile.bucket.BucketCache.DEFAULT_ERROR_TOLERATION_DURATION;
2222
import static org.junit.Assert.assertEquals;
23+
import static org.junit.Assert.assertFalse;
24+
import static org.junit.Assert.assertNull;
2325
import static org.junit.Assert.assertTrue;
2426
import static org.junit.Assert.fail;
2527

@@ -66,6 +68,8 @@
6668
import org.junit.ClassRule;
6769
import org.junit.Test;
6870
import org.junit.experimental.categories.Category;
71+
import org.slf4j.Logger;
72+
import org.slf4j.LoggerFactory;
6973

7074
/**
7175
* This class is used to test the functionality of the DataTieringManager.
@@ -93,6 +97,7 @@ public class TestDataTieringManager {
9397
public static final HBaseClassTestRule CLASS_RULE =
9498
HBaseClassTestRule.forClass(TestDataTieringManager.class);
9599

100+
private static final Logger LOG = LoggerFactory.getLogger(TestDataTieringManager.class);
96101
private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
97102
private static Configuration defaultConf;
98103
private static FileSystem fs;
@@ -111,10 +116,11 @@ public static void setupBeforeClass() throws Exception {
111116
defaultConf.setBoolean(CacheConfig.PREFETCH_BLOCKS_ON_OPEN_KEY, true);
112117
defaultConf.setStrings(HConstants.BUCKET_CACHE_IOENGINE_KEY, "offheap");
113118
defaultConf.setLong(BUCKET_CACHE_SIZE_KEY, 32);
119+
defaultConf.setBoolean(DataTieringManager.GLOBAL_DATA_TIERING_ENABLED_KEY, true);
114120
fs = HFileSystem.get(defaultConf);
115121
blockCache = BlockCacheFactory.createBlockCache(defaultConf);
116122
cacheConf = new CacheConfig(defaultConf, blockCache);
117-
DataTieringManager.instantiate(testOnlineRegions);
123+
assertTrue(DataTieringManager.instantiate(defaultConf, testOnlineRegions));
118124
setupOnlineRegions();
119125
dataTieringManager = DataTieringManager.getInstance();
120126
}
@@ -270,9 +276,9 @@ public void testBlockEvictions() throws Exception {
270276
int[] bucketSizes = new int[] { 8 * 1024 + 1024 };
271277

272278
// Setup: Create a bucket cache with lower capacity
273-
BucketCache bucketCache = new BucketCache("file:" + testDir + "/bucket.cache", capacitySize,
274-
8192, bucketSizes, writeThreads, writerQLen, testDir + "/bucket.persistence",
275-
DEFAULT_ERROR_TOLERATION_DURATION, defaultConf);
279+
BucketCache bucketCache =
280+
new BucketCache("file:" + testDir + "/bucket.cache", capacitySize, 8192, bucketSizes,
281+
writeThreads, writerQLen, null, DEFAULT_ERROR_TOLERATION_DURATION, defaultConf);
276282

277283
// Create three Cache keys with cold data files and a block with hot data.
278284
// hStoreFiles.get(3) is a cold data file, while hStoreFiles.get(0) is a hot file.
@@ -320,9 +326,9 @@ public void testBlockEvictionsAllColdBlocks() throws Exception {
320326
int[] bucketSizes = new int[] { 8 * 1024 + 1024 };
321327

322328
// Setup: Create a bucket cache with lower capacity
323-
BucketCache bucketCache = new BucketCache("file:" + testDir + "/bucket.cache", capacitySize,
324-
8192, bucketSizes, writeThreads, writerQLen, testDir + "/bucket.persistence",
325-
DEFAULT_ERROR_TOLERATION_DURATION, defaultConf);
329+
BucketCache bucketCache =
330+
new BucketCache("file:" + testDir + "/bucket.cache", capacitySize, 8192, bucketSizes,
331+
writeThreads, writerQLen, null, DEFAULT_ERROR_TOLERATION_DURATION, defaultConf);
326332

327333
// Create three Cache keys with three cold data blocks.
328334
// hStoreFiles.get(3) is a cold data file.
@@ -367,9 +373,9 @@ public void testBlockEvictionsHotBlocks() throws Exception {
367373
int[] bucketSizes = new int[] { 8 * 1024 + 1024 };
368374

369375
// Setup: Create a bucket cache with lower capacity
370-
BucketCache bucketCache = new BucketCache("file:" + testDir + "/bucket.cache", capacitySize,
371-
8192, bucketSizes, writeThreads, writerQLen, testDir + "/bucket.persistence",
372-
DEFAULT_ERROR_TOLERATION_DURATION, defaultConf);
376+
BucketCache bucketCache =
377+
new BucketCache("file:" + testDir + "/bucket.cache", capacitySize, 8192, bucketSizes,
378+
writeThreads, writerQLen, null, DEFAULT_ERROR_TOLERATION_DURATION, defaultConf);
373379

374380
// Create three Cache keys with two hot data blocks and one cold data block
375381
// hStoreFiles.get(0) is a hot data file and hStoreFiles.get(3) is a cold data file.
@@ -404,11 +410,74 @@ public void testBlockEvictionsHotBlocks() throws Exception {
404410
validateBlocks(bucketCache.getBackingMap().keySet(), 2, 2, 0);
405411
}
406412

413+
@Test
414+
public void testFeatureKeyDisabled() throws Exception {
415+
DataTieringManager.resetForTestingOnly();
416+
defaultConf.setBoolean(DataTieringManager.GLOBAL_DATA_TIERING_ENABLED_KEY, false);
417+
try {
418+
assertFalse(DataTieringManager.instantiate(defaultConf, testOnlineRegions));
419+
// Verify that the DataaTieringManager instance is not instantiated in the
420+
// instantiate call above.
421+
assertNull(DataTieringManager.getInstance());
422+
423+
// Also validate that data temperature is not honoured.
424+
long capacitySize = 40 * 1024;
425+
int writeThreads = 3;
426+
int writerQLen = 64;
427+
int[] bucketSizes = new int[] { 8 * 1024 + 1024 };
428+
429+
// Setup: Create a bucket cache with lower capacity
430+
BucketCache bucketCache =
431+
new BucketCache("file:" + testDir + "/bucket.cache", capacitySize, 8192, bucketSizes,
432+
writeThreads, writerQLen, null, DEFAULT_ERROR_TOLERATION_DURATION, defaultConf);
433+
434+
// Create three Cache keys with two hot data blocks and one cold data block
435+
// hStoreFiles.get(0) is a hot data file and hStoreFiles.get(3) is a cold data file.
436+
List<BlockCacheKey> cacheKeys = new ArrayList<>();
437+
cacheKeys.add(new BlockCacheKey(hStoreFiles.get(0).getPath(), 0, true, BlockType.DATA));
438+
cacheKeys.add(new BlockCacheKey(hStoreFiles.get(0).getPath(), 8192, true, BlockType.DATA));
439+
cacheKeys.add(new BlockCacheKey(hStoreFiles.get(3).getPath(), 0, true, BlockType.DATA));
440+
441+
// Create dummy data to be cached and fill the cache completely.
442+
CacheTestUtils.HFileBlockPair[] blocks = CacheTestUtils.generateHFileBlocks(8192, 3);
443+
444+
int blocksIter = 0;
445+
for (BlockCacheKey key : cacheKeys) {
446+
LOG.info("Adding {}", key);
447+
bucketCache.cacheBlock(key, blocks[blocksIter++].getBlock());
448+
// Ensure that the block is persisted to the file.
449+
Waiter.waitFor(defaultConf, 10000, 100,
450+
() -> (bucketCache.getBackingMap().containsKey(key)));
451+
}
452+
453+
// Verify that the bucket cache contains 3 blocks.
454+
assertEquals(3, bucketCache.getBackingMap().keySet().size());
455+
456+
// Add an additional hot block, which triggers eviction.
457+
BlockCacheKey newKey =
458+
new BlockCacheKey(hStoreFiles.get(2).getPath(), 0, true, BlockType.DATA);
459+
CacheTestUtils.HFileBlockPair[] newBlock = CacheTestUtils.generateHFileBlocks(8192, 1);
460+
461+
bucketCache.cacheBlock(newKey, newBlock[0].getBlock());
462+
Waiter.waitFor(defaultConf, 10000, 100,
463+
() -> (bucketCache.getBackingMap().containsKey(newKey)));
464+
465+
// Verify that the bucket still contains the only cold block and one newly added hot block.
466+
// The older hot blocks are evicted and data-tiering mechanism does not kick in to evict
467+
// the cold block.
468+
validateBlocks(bucketCache.getBackingMap().keySet(), 2, 1, 1);
469+
} finally {
470+
DataTieringManager.resetForTestingOnly();
471+
defaultConf.setBoolean(DataTieringManager.GLOBAL_DATA_TIERING_ENABLED_KEY, true);
472+
assertTrue(DataTieringManager.instantiate(defaultConf, testOnlineRegions));
473+
}
474+
}
475+
407476
private void validateBlocks(Set<BlockCacheKey> keys, int expectedTotalKeys, int expectedHotBlocks,
408477
int expectedColdBlocks) {
409478
int numHotBlocks = 0, numColdBlocks = 0;
410479

411-
assertEquals(expectedTotalKeys, keys.size());
480+
Waiter.waitFor(defaultConf, 10000, 100, () -> (expectedTotalKeys == keys.size()));
412481
int iter = 0;
413482
for (BlockCacheKey key : keys) {
414483
try {

0 commit comments

Comments
 (0)