Skip to content

Commit 3f4734e

Browse files
wchevreuilJosh Elser
andauthored
HBASE-27370 Avoid decompressing blocks when reading from bucket cache… (#4781)
Co-authored-by: Josh Elser <elserj@apache.com> Signed-off-by: Peter Somogyi <psomogyi@apache.org> Signed-off-by: Tak Lon (Stephen) Wu <taklwu@apache.org>
1 parent 3f5c0a5 commit 3f4734e

File tree

5 files changed

+118
-23
lines changed

5 files changed

+118
-23
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,10 @@ public interface CachingBlockReader {
373373
HFileBlock readBlock(long offset, long onDiskBlockSize, boolean cacheBlock, final boolean pread,
374374
final boolean isCompaction, final boolean updateCacheMetrics, BlockType expectedBlockType,
375375
DataBlockEncoding expectedDataBlockEncoding) throws IOException;
376+
377+
HFileBlock readBlock(long offset, long onDiskBlockSize, boolean cacheBlock, final boolean pread,
378+
final boolean isCompaction, final boolean updateCacheMetrics, BlockType expectedBlockType,
379+
DataBlockEncoding expectedDataBlockEncoding, boolean cacheOnly) throws IOException;
376380
}
377381

378382
/** An interface used by clients to open and iterate an {@link HFile}. */

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public void run() {
5757
// next header, will not have happened...so, pass in the onDiskSize gotten from the
5858
// cached block. This 'optimization' triggers extremely rarely I'd say.
5959
HFileBlock block = readBlock(offset, onDiskSizeOfNextBlock, /* cacheBlock= */true,
60-
/* pread= */true, false, false, null, null);
60+
/* pread= */true, false, false, null, null, true);
6161
try {
6262
onDiskSizeOfNextBlock = block.getNextBlockOnDiskSize();
6363
offset += block.getOnDiskSizeWithHeader();

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

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,7 @@ public void setConf(Configuration conf) {
10841084
* and its encoding vs. {@code expectedDataBlockEncoding}. Unpacks the block as necessary.
10851085
*/
10861086
private HFileBlock getCachedBlock(BlockCacheKey cacheKey, boolean cacheBlock, boolean useLock,
1087-
boolean isCompaction, boolean updateCacheMetrics, BlockType expectedBlockType,
1087+
boolean updateCacheMetrics, BlockType expectedBlockType,
10881088
DataBlockEncoding expectedDataBlockEncoding) throws IOException {
10891089
// Check cache for block. If found return.
10901090
BlockCache cache = cacheConf.getBlockCache().orElse(null);
@@ -1189,7 +1189,7 @@ public HFileBlock getMetaBlock(String metaBlockName, boolean cacheBlock) throws
11891189

11901190
cacheBlock &= cacheConf.shouldCacheBlockOnRead(BlockType.META.getCategory());
11911191
HFileBlock cachedBlock =
1192-
getCachedBlock(cacheKey, cacheBlock, false, true, true, BlockType.META, null);
1192+
getCachedBlock(cacheKey, cacheBlock, false, true, BlockType.META, null);
11931193
if (cachedBlock != null) {
11941194
assert cachedBlock.isUnpacked() : "Packed block leak.";
11951195
// Return a distinct 'shallow copy' of the block,
@@ -1236,6 +1236,15 @@ private boolean shouldUseHeap(BlockType expectedBlockType) {
12361236
public HFileBlock readBlock(long dataBlockOffset, long onDiskBlockSize, final boolean cacheBlock,
12371237
boolean pread, final boolean isCompaction, boolean updateCacheMetrics,
12381238
BlockType expectedBlockType, DataBlockEncoding expectedDataBlockEncoding) throws IOException {
1239+
return readBlock(dataBlockOffset, onDiskBlockSize, cacheBlock, pread, isCompaction,
1240+
updateCacheMetrics, expectedBlockType, expectedDataBlockEncoding, false);
1241+
}
1242+
1243+
@Override
1244+
public HFileBlock readBlock(long dataBlockOffset, long onDiskBlockSize, final boolean cacheBlock,
1245+
boolean pread, final boolean isCompaction, boolean updateCacheMetrics,
1246+
BlockType expectedBlockType, DataBlockEncoding expectedDataBlockEncoding, boolean cacheOnly)
1247+
throws IOException {
12391248
if (dataBlockIndexReader == null) {
12401249
throw new IOException(path + " block index not loaded");
12411250
}
@@ -1261,17 +1270,18 @@ public HFileBlock readBlock(long dataBlockOffset, long onDiskBlockSize, final bo
12611270
try {
12621271
while (true) {
12631272
// Check cache for block. If found return.
1264-
if (cacheConf.shouldReadBlockFromCache(expectedBlockType)) {
1273+
if (cacheConf.shouldReadBlockFromCache(expectedBlockType) && !cacheOnly) {
12651274
if (useLock) {
12661275
lockEntry = offsetLock.getLockEntry(dataBlockOffset);
12671276
}
12681277
// Try and get the block from the block cache. If the useLock variable is true then this
12691278
// is the second time through the loop and it should not be counted as a block cache miss.
1270-
HFileBlock cachedBlock = getCachedBlock(cacheKey, cacheBlock, useLock, isCompaction,
1271-
updateCacheMetrics, expectedBlockType, expectedDataBlockEncoding);
1279+
HFileBlock cachedBlock = getCachedBlock(cacheKey, cacheBlock, useLock, updateCacheMetrics,
1280+
expectedBlockType, expectedDataBlockEncoding);
12721281
if (cachedBlock != null) {
12731282
if (LOG.isTraceEnabled()) {
1274-
LOG.trace("From Cache {}", cachedBlock);
1283+
LOG.trace("Block for file {} is coming from Cache {}",
1284+
Bytes.toString(cachedBlock.getHFileContext().getTableName()), cachedBlock);
12751285
}
12761286
span.addEvent("block cache hit", attributes);
12771287
assert cachedBlock.isUnpacked() : "Packed block leak.";
@@ -1308,14 +1318,30 @@ public HFileBlock readBlock(long dataBlockOffset, long onDiskBlockSize, final bo
13081318
HFileBlock hfileBlock = fsBlockReader.readBlockData(dataBlockOffset, onDiskBlockSize, pread,
13091319
!isCompaction, shouldUseHeap(expectedBlockType));
13101320
validateBlockType(hfileBlock, expectedBlockType);
1311-
HFileBlock unpacked = hfileBlock.unpack(hfileContext, fsBlockReader);
13121321
BlockType.BlockCategory category = hfileBlock.getBlockType().getCategory();
1322+
final boolean cacheCompressed = cacheConf.shouldCacheCompressed(category);
1323+
final boolean cacheOnRead = cacheConf.shouldCacheBlockOnRead(category);
1324+
1325+
// Don't need the unpacked block back and we're storing the block in the cache compressed
1326+
if (cacheOnly && cacheCompressed && cacheOnRead) {
1327+
LOG.debug("Skipping decompression of block in prefetch");
1328+
// Cache the block if necessary
1329+
cacheConf.getBlockCache().ifPresent(cache -> {
1330+
if (cacheBlock && cacheConf.shouldCacheBlockOnRead(category)) {
1331+
cache.cacheBlock(cacheKey, hfileBlock, cacheConf.isInMemory());
1332+
}
1333+
});
13131334

1335+
if (updateCacheMetrics && hfileBlock.getBlockType().isData()) {
1336+
HFile.DATABLOCK_READ_COUNT.increment();
1337+
}
1338+
return hfileBlock;
1339+
}
1340+
HFileBlock unpacked = hfileBlock.unpack(hfileContext, fsBlockReader);
13141341
// Cache the block if necessary
13151342
cacheConf.getBlockCache().ifPresent(cache -> {
13161343
if (cacheBlock && cacheConf.shouldCacheBlockOnRead(category)) {
1317-
cache.cacheBlock(cacheKey,
1318-
cacheConf.shouldCacheCompressed(category) ? hfileBlock : unpacked,
1344+
cache.cacheBlock(cacheKey, cacheCompressed ? hfileBlock : unpacked,
13191345
cacheConf.isInMemory());
13201346
}
13211347
});

hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestHFileBlockIndex.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,14 @@ public BlockReaderWrapper(HFileBlock.FSReader realReader) {
178178
public HFileBlock readBlock(long offset, long onDiskSize, boolean cacheBlock, boolean pread,
179179
boolean isCompaction, boolean updateCacheMetrics, BlockType expectedBlockType,
180180
DataBlockEncoding expectedDataBlockEncoding) throws IOException {
181+
return readBlock(offset, onDiskSize, cacheBlock, pread, isCompaction, updateCacheMetrics,
182+
expectedBlockType, expectedDataBlockEncoding, false);
183+
}
184+
185+
@Override
186+
public HFileBlock readBlock(long offset, long onDiskSize, boolean cacheBlock, boolean pread,
187+
boolean isCompaction, boolean updateCacheMetrics, BlockType expectedBlockType,
188+
DataBlockEncoding expectedDataBlockEncoding, boolean cacheOnly) throws IOException {
181189
if (offset == prevOffset && onDiskSize == prevOnDiskSize && pread == prevPread) {
182190
hitCount += 1;
183191
return prevBlock;

hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestPrefetch.java

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919

2020
import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasName;
2121
import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasParentSpanId;
22+
import static org.apache.hadoop.hbase.io.hfile.CacheConfig.CACHE_DATA_BLOCKS_COMPRESSED_KEY;
2223
import static org.hamcrest.MatcherAssert.assertThat;
2324
import static org.hamcrest.Matchers.allOf;
2425
import static org.hamcrest.Matchers.hasItem;
2526
import static org.hamcrest.Matchers.hasItems;
2627
import static org.hamcrest.Matchers.not;
2728
import static org.junit.Assert.assertFalse;
2829
import static org.junit.Assert.assertTrue;
30+
import static org.junit.Assert.fail;
2931

3032
import io.opentelemetry.sdk.testing.junit4.OpenTelemetryRule;
3133
import io.opentelemetry.sdk.trace.data.SpanData;
@@ -34,6 +36,8 @@
3436
import java.util.Random;
3537
import java.util.concurrent.ThreadLocalRandom;
3638
import java.util.concurrent.TimeUnit;
39+
import java.util.function.BiConsumer;
40+
import java.util.function.BiFunction;
3741
import org.apache.hadoop.conf.Configuration;
3842
import org.apache.hadoop.fs.FileSystem;
3943
import org.apache.hadoop.fs.Path;
@@ -47,6 +51,7 @@
4751
import org.apache.hadoop.hbase.client.trace.StringTraceRenderer;
4852
import org.apache.hadoop.hbase.fs.HFileSystem;
4953
import org.apache.hadoop.hbase.io.ByteBuffAllocator;
54+
import org.apache.hadoop.hbase.io.compress.Compression;
5055
import org.apache.hadoop.hbase.regionserver.StoreFileWriter;
5156
import org.apache.hadoop.hbase.testclassification.IOTests;
5257
import org.apache.hadoop.hbase.testclassification.MediumTests;
@@ -148,36 +153,88 @@ private void readStoreFileLikeScanner(Path storeFilePath) throws Exception {
148153
}
149154

150155
private void readStoreFile(Path storeFilePath) throws Exception {
156+
readStoreFile(storeFilePath, (r, o) -> {
157+
HFileBlock block = null;
158+
try {
159+
block = r.readBlock(o, -1, false, true, false, true, null, null);
160+
} catch (IOException e) {
161+
fail(e.getMessage());
162+
}
163+
return block;
164+
}, (key, block) -> {
165+
boolean isCached = blockCache.getBlock(key, true, false, true) != null;
166+
if (
167+
block.getBlockType() == BlockType.DATA || block.getBlockType() == BlockType.ROOT_INDEX
168+
|| block.getBlockType() == BlockType.INTERMEDIATE_INDEX
169+
) {
170+
assertTrue(isCached);
171+
}
172+
});
173+
}
174+
175+
private void readStoreFileCacheOnly(Path storeFilePath) throws Exception {
176+
readStoreFile(storeFilePath, (r, o) -> {
177+
HFileBlock block = null;
178+
try {
179+
block = r.readBlock(o, -1, false, true, false, true, null, null, true);
180+
} catch (IOException e) {
181+
fail(e.getMessage());
182+
}
183+
return block;
184+
}, (key, block) -> {
185+
boolean isCached = blockCache.getBlock(key, true, false, true) != null;
186+
if (block.getBlockType() == BlockType.DATA) {
187+
assertFalse(block.isUnpacked());
188+
} else if (
189+
block.getBlockType() == BlockType.ROOT_INDEX
190+
|| block.getBlockType() == BlockType.INTERMEDIATE_INDEX
191+
) {
192+
assertTrue(block.isUnpacked());
193+
}
194+
assertTrue(isCached);
195+
});
196+
}
197+
198+
private void readStoreFile(Path storeFilePath,
199+
BiFunction<HFile.Reader, Long, HFileBlock> readFunction,
200+
BiConsumer<BlockCacheKey, HFileBlock> validationFunction) throws Exception {
151201
// Open the file
152202
HFile.Reader reader = HFile.createReader(fs, storeFilePath, cacheConf, true, conf);
153203

154204
while (!reader.prefetchComplete()) {
155205
// Sleep for a bit
156206
Thread.sleep(1000);
157207
}
158-
159-
// Check that all of the data blocks were preloaded
160-
BlockCache blockCache = cacheConf.getBlockCache().get();
161208
long offset = 0;
162209
while (offset < reader.getTrailer().getLoadOnOpenDataOffset()) {
163-
HFileBlock block = reader.readBlock(offset, -1, false, true, false, true, null, null);
210+
HFileBlock block = readFunction.apply(reader, offset);
164211
BlockCacheKey blockCacheKey = new BlockCacheKey(reader.getName(), offset);
165-
boolean isCached = blockCache.getBlock(blockCacheKey, true, false, true) != null;
166-
if (
167-
block.getBlockType() == BlockType.DATA || block.getBlockType() == BlockType.ROOT_INDEX
168-
|| block.getBlockType() == BlockType.INTERMEDIATE_INDEX
169-
) {
170-
assertTrue(isCached);
171-
}
212+
validationFunction.accept(blockCacheKey, block);
172213
offset += block.getOnDiskSizeWithHeader();
173214
}
174215
}
175216

217+
@Test
218+
public void testPrefetchCompressed() throws Exception {
219+
conf.setBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, true);
220+
cacheConf = new CacheConfig(conf, blockCache);
221+
HFileContext context = new HFileContextBuilder().withCompression(Compression.Algorithm.GZ)
222+
.withBlockSize(DATA_BLOCK_SIZE).build();
223+
Path storeFile = writeStoreFile("TestPrefetchCompressed", context);
224+
readStoreFileCacheOnly(storeFile);
225+
conf.setBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, false);
226+
227+
}
228+
176229
private Path writeStoreFile(String fname) throws IOException {
177-
Path storeFileParentDir = new Path(TEST_UTIL.getDataTestDir(), fname);
178230
HFileContext meta = new HFileContextBuilder().withBlockSize(DATA_BLOCK_SIZE).build();
231+
return writeStoreFile(fname, meta);
232+
}
233+
234+
private Path writeStoreFile(String fname, HFileContext context) throws IOException {
235+
Path storeFileParentDir = new Path(TEST_UTIL.getDataTestDir(), fname);
179236
StoreFileWriter sfw = new StoreFileWriter.Builder(conf, cacheConf, fs)
180-
.withOutputDir(storeFileParentDir).withFileContext(meta).build();
237+
.withOutputDir(storeFileParentDir).withFileContext(context).build();
181238
Random rand = ThreadLocalRandom.current();
182239
final int rowLen = 32;
183240
for (int i = 0; i < NUM_KV; ++i) {

0 commit comments

Comments
 (0)