Skip to content

Commit d140e05

Browse files
committed
HBASE-22491 Separate the heap HFileBlock and offheap HFileBlock because the heap block won't need refCnt and save into prevBlocks list before shipping
1 parent 68c5129 commit d140e05

File tree

15 files changed

+616
-291
lines changed

15 files changed

+616
-291
lines changed

hbase-common/src/main/java/org/apache/hadoop/hbase/io/ByteBuffAllocator.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,12 @@ public void clean() {
281281
}
282282
}
283283
}
284+
this.usedBufCount.set(0);
285+
this.maxPoolSizeInfoLevelLogged = false;
286+
this.poolAllocationBytes.reset();
287+
this.heapAllocationBytes.reset();
288+
this.lastPoolAllocationBytes = 0;
289+
this.lastHeapAllocationBytes = 0;
284290
}
285291

286292
/**

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

Lines changed: 79 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ public HFileBlock deserialize(ByteBuff buf, ByteBuffAllocator alloc)
285285
boolean usesChecksum = buf.get() == (byte) 1;
286286
long offset = buf.getLong();
287287
int nextBlockOnDiskSize = buf.getInt();
288-
return new HFileBlock(newByteBuff, usesChecksum, offset, nextBlockOnDiskSize, null, alloc);
288+
return createFromBuff(newByteBuff, usesChecksum, offset, nextBlockOnDiskSize, null, alloc);
289289
}
290290

291291
@Override
@@ -300,28 +300,6 @@ public int getDeserializerIdentifier() {
300300
CacheableDeserializerIdManager.registerDeserializer(BLOCK_DESERIALIZER);
301301
}
302302

303-
/**
304-
* Copy constructor. Creates a shallow copy of {@code that}'s buffer.
305-
*/
306-
private HFileBlock(HFileBlock that) {
307-
this(that, false);
308-
}
309-
310-
/**
311-
* Copy constructor. Creates a shallow/deep copy of {@code that}'s buffer as per the boolean
312-
* param.
313-
*/
314-
private HFileBlock(HFileBlock that, boolean bufCopy) {
315-
init(that.blockType, that.onDiskSizeWithoutHeader, that.uncompressedSizeWithoutHeader,
316-
that.prevBlockOffset, that.offset, that.onDiskDataSizeWithHeader, that.nextBlockOnDiskSize,
317-
that.fileContext, that.allocator);
318-
if (bufCopy) {
319-
this.buf = ByteBuff.wrap(ByteBuffer.wrap(that.buf.toBytes(0, that.buf.limit())));
320-
} else {
321-
this.buf = that.buf.duplicate();
322-
}
323-
}
324-
325303
/**
326304
* Creates a new {@link HFile} block from the given fields. This constructor
327305
* is used only while writing blocks and caching,
@@ -336,20 +314,27 @@ private HFileBlock(HFileBlock that, boolean bufCopy) {
336314
* @param onDiskSizeWithoutHeader see {@link #onDiskSizeWithoutHeader}
337315
* @param uncompressedSizeWithoutHeader see {@link #uncompressedSizeWithoutHeader}
338316
* @param prevBlockOffset see {@link #prevBlockOffset}
339-
* @param b block header ({@link HConstants#HFILEBLOCK_HEADER_SIZE} bytes)
317+
* @param buf block buffer with header ({@link HConstants#HFILEBLOCK_HEADER_SIZE} bytes)
340318
* @param fillHeader when true, write the first 4 header fields into passed buffer.
341319
* @param offset the file offset the block was read from
342320
* @param onDiskDataSizeWithHeader see {@link #onDiskDataSizeWithHeader}
343321
* @param fileContext HFile meta data
344322
*/
345323
@VisibleForTesting
346324
public HFileBlock(BlockType blockType, int onDiskSizeWithoutHeader,
347-
int uncompressedSizeWithoutHeader, long prevBlockOffset, ByteBuffer b, boolean fillHeader,
348-
long offset, final int nextBlockOnDiskSize, int onDiskDataSizeWithHeader,
349-
HFileContext fileContext, ByteBuffAllocator allocator) {
350-
init(blockType, onDiskSizeWithoutHeader, uncompressedSizeWithoutHeader, prevBlockOffset, offset,
351-
onDiskDataSizeWithHeader, nextBlockOnDiskSize, fileContext, allocator);
352-
this.buf = new SingleByteBuff(b);
325+
int uncompressedSizeWithoutHeader, long prevBlockOffset, ByteBuff buf, boolean fillHeader,
326+
long offset, int nextBlockOnDiskSize, int onDiskDataSizeWithHeader, HFileContext fileContext,
327+
ByteBuffAllocator allocator) {
328+
this.blockType = blockType;
329+
this.onDiskSizeWithoutHeader = onDiskSizeWithoutHeader;
330+
this.uncompressedSizeWithoutHeader = uncompressedSizeWithoutHeader;
331+
this.prevBlockOffset = prevBlockOffset;
332+
this.offset = offset;
333+
this.onDiskDataSizeWithHeader = onDiskDataSizeWithHeader;
334+
this.nextBlockOnDiskSize = nextBlockOnDiskSize;
335+
this.fileContext = fileContext;
336+
this.allocator = allocator;
337+
this.buf = buf;
353338
if (fillHeader) {
354339
overwriteHeader();
355340
}
@@ -363,7 +348,7 @@ public HFileBlock(BlockType blockType, int onDiskSizeWithoutHeader,
363348
* to that point.
364349
* @param buf Has header, content, and trailing checksums if present.
365350
*/
366-
HFileBlock(ByteBuff buf, boolean usesHBaseChecksum, final long offset,
351+
static HFileBlock createFromBuff(ByteBuff buf, boolean usesHBaseChecksum, final long offset,
367352
final int nextBlockOnDiskSize, HFileContext fileContext, ByteBuffAllocator allocator)
368353
throws IOException {
369354
buf.rewind();
@@ -374,15 +359,15 @@ public HFileBlock(BlockType blockType, int onDiskSizeWithoutHeader,
374359
final long prevBlockOffset = buf.getLong(Header.PREV_BLOCK_OFFSET_INDEX);
375360
// This constructor is called when we deserialize a block from cache and when we read a block in
376361
// from the fs. fileCache is null when deserialized from cache so need to make up one.
377-
HFileContextBuilder fileContextBuilder = fileContext != null?
378-
new HFileContextBuilder(fileContext): new HFileContextBuilder();
362+
HFileContextBuilder fileContextBuilder =
363+
fileContext != null ? new HFileContextBuilder(fileContext) : new HFileContextBuilder();
379364
fileContextBuilder.withHBaseCheckSum(usesHBaseChecksum);
380365
int onDiskDataSizeWithHeader;
381366
if (usesHBaseChecksum) {
382367
byte checksumType = buf.get(Header.CHECKSUM_TYPE_INDEX);
383368
int bytesPerChecksum = buf.getInt(Header.BYTES_PER_CHECKSUM_INDEX);
384369
onDiskDataSizeWithHeader = buf.getInt(Header.ON_DISK_DATA_SIZE_WITH_HEADER_INDEX);
385-
// Use the checksum type and bytes per checksum from header, not from filecontext.
370+
// Use the checksum type and bytes per checksum from header, not from fileContext.
386371
fileContextBuilder.withChecksumType(ChecksumType.codeToType(checksumType));
387372
fileContextBuilder.withBytesPerCheckSum(bytesPerChecksum);
388373
} else {
@@ -393,29 +378,19 @@ public HFileBlock(BlockType blockType, int onDiskSizeWithoutHeader,
393378
}
394379
fileContext = fileContextBuilder.build();
395380
assert usesHBaseChecksum == fileContext.isUseHBaseChecksum();
396-
init(blockType, onDiskSizeWithoutHeader, uncompressedSizeWithoutHeader, prevBlockOffset, offset,
397-
onDiskDataSizeWithHeader, nextBlockOnDiskSize, fileContext, allocator);
398-
this.offset = offset;
399-
this.buf = buf;
400-
this.buf.rewind();
401-
}
402-
403-
/**
404-
* Called from constructors.
405-
*/
406-
private void init(BlockType blockType, int onDiskSizeWithoutHeader,
407-
int uncompressedSizeWithoutHeader, long prevBlockOffset, long offset,
408-
int onDiskDataSizeWithHeader, final int nextBlockOnDiskSize, HFileContext fileContext,
409-
ByteBuffAllocator allocator) {
410-
this.blockType = blockType;
411-
this.onDiskSizeWithoutHeader = onDiskSizeWithoutHeader;
412-
this.uncompressedSizeWithoutHeader = uncompressedSizeWithoutHeader;
413-
this.prevBlockOffset = prevBlockOffset;
414-
this.offset = offset;
415-
this.onDiskDataSizeWithHeader = onDiskDataSizeWithHeader;
416-
this.nextBlockOnDiskSize = nextBlockOnDiskSize;
417-
this.fileContext = fileContext;
418-
this.allocator = allocator;
381+
return new HFileBlockBuilder()
382+
.withBlockType(blockType)
383+
.withOnDiskSizeWithoutHeader(onDiskSizeWithoutHeader)
384+
.withUncompressedSizeWithoutHeader(uncompressedSizeWithoutHeader)
385+
.withPrevBlockOffset(prevBlockOffset)
386+
.withOffset(offset)
387+
.withOnDiskDataSizeWithHeader(onDiskDataSizeWithHeader)
388+
.withNextBlockOnDiskSize(nextBlockOnDiskSize)
389+
.withHFileContext(fileContext)
390+
.withByteBuffAllocator(allocator)
391+
.withOffset(offset)
392+
.withByteBuff(buf.rewind())
393+
.build();
419394
}
420395

421396
/**
@@ -639,7 +614,7 @@ public String toString() {
639614
.append("(").append(onDiskSizeWithoutHeader)
640615
.append("+").append(HConstants.HFILEBLOCK_HEADER_SIZE_NO_CHECKSUM).append(")");
641616
}
642-
String dataBegin = null;
617+
String dataBegin;
643618
if (buf.hasArray()) {
644619
dataBegin = Bytes.toStringBinary(buf.array(), buf.arrayOffset() + headerSize(),
645620
Math.min(32, buf.limit() - buf.arrayOffset() - headerSize()));
@@ -673,7 +648,7 @@ HFileBlock unpack(HFileContext fileContext, FSReader reader) throws IOException
673648
return this;
674649
}
675650

676-
HFileBlock unpacked = new HFileBlock(this);
651+
HFileBlock unpacked = shallowClone(this);
677652
unpacked.allocateBuffer(); // allocates space for the decompressed block
678653
boolean succ = false;
679654
try {
@@ -763,7 +738,7 @@ public long heapSize() {
763738
/**
764739
* @return true to indicate the block is allocated from JVM heap, otherwise from off-heap.
765740
*/
766-
boolean isOnHeap() {
741+
public boolean isOnHeap() {
767742
return buf.hasArray();
768743
}
769744

@@ -1039,8 +1014,7 @@ void writeHeaderAndData(FSDataOutputStream out) throws IOException {
10391014
+ offset);
10401015
}
10411016
startOffset = offset;
1042-
1043-
finishBlockAndWriteHeaderAndData((DataOutputStream) out);
1017+
finishBlockAndWriteHeaderAndData(out);
10441018
}
10451019

10461020
/**
@@ -1251,13 +1225,26 @@ HFileBlock getBlockForCaching(CacheConfig cacheConf) {
12511225
.withIncludesMvcc(fileContext.isIncludesMvcc())
12521226
.withIncludesTags(fileContext.isIncludesTags())
12531227
.build();
1254-
return new HFileBlock(blockType, getOnDiskSizeWithoutHeader(),
1255-
getUncompressedSizeWithoutHeader(), prevOffset,
1256-
cacheConf.shouldCacheCompressed(blockType.getCategory()) ? cloneOnDiskBufferWithHeader()
1257-
: cloneUncompressedBufferWithHeader(),
1258-
FILL_HEADER, startOffset, UNSET,
1259-
onDiskBlockBytesWithHeader.size() + onDiskChecksum.length, newContext,
1260-
cacheConf.getByteBuffAllocator());
1228+
// Build the HFileBlock.
1229+
HFileBlockBuilder builder = new HFileBlockBuilder();
1230+
ByteBuffer buffer;
1231+
if (cacheConf.shouldCacheCompressed(blockType.getCategory())) {
1232+
buffer = cloneOnDiskBufferWithHeader();
1233+
} else {
1234+
buffer = cloneUncompressedBufferWithHeader();
1235+
}
1236+
return builder.withBlockType(blockType)
1237+
.withOnDiskSizeWithoutHeader(getOnDiskSizeWithoutHeader())
1238+
.withUncompressedSizeWithoutHeader(getUncompressedSizeWithoutHeader())
1239+
.withPrevBlockOffset(prevOffset)
1240+
.withByteBuff(ByteBuff.wrap(buffer))
1241+
.withFillHeader(FILL_HEADER)
1242+
.withOffset(startOffset)
1243+
.withNextBlockOnDiskSize(UNSET)
1244+
.withOnDiskDataSizeWithHeader(onDiskBlockBytesWithHeader.size() + onDiskChecksum.length)
1245+
.withHFileContext(newContext)
1246+
.withByteBuffAllocator(cacheConf.getByteBuffAllocator())
1247+
.build();
12611248
}
12621249
}
12631250

@@ -1781,8 +1768,8 @@ protected HFileBlock readBlockDataInternal(FSDataInputStream is, long offset,
17811768
// The onDiskBlock will become the headerAndDataBuffer for this block.
17821769
// If nextBlockOnDiskSizeWithHeader is not zero, the onDiskBlock already
17831770
// contains the header of next block, so no need to set next block's header in it.
1784-
HFileBlock hFileBlock = new HFileBlock(curBlock, checksumSupport, offset,
1785-
nextBlockOnDiskSize, fileContext, intoHeap ? HEAP : allocator);
1771+
HFileBlock hFileBlock = createFromBuff(curBlock, checksumSupport, offset,
1772+
nextBlockOnDiskSize, fileContext, intoHeap ? HEAP : allocator);
17861773
// Run check on uncompressed sizings.
17871774
if (!fileContext.isCompressedOrEncrypted()) {
17881775
hFileBlock.sanityCheckUncompressed();
@@ -2084,7 +2071,26 @@ static String toStringHeader(ByteBuff buf) throws IOException {
20842071
" onDiskDataSizeWithHeader " + onDiskDataSizeWithHeader;
20852072
}
20862073

2087-
public HFileBlock deepCloneOnHeap() {
2088-
return new HFileBlock(this, true);
2074+
private static HFileBlockBuilder createBuilder(HFileBlock blk){
2075+
return new HFileBlockBuilder()
2076+
.withBlockType(blk.blockType)
2077+
.withOnDiskSizeWithoutHeader(blk.onDiskSizeWithoutHeader)
2078+
.withUncompressedSizeWithoutHeader(blk.uncompressedSizeWithoutHeader)
2079+
.withPrevBlockOffset(blk.prevBlockOffset)
2080+
.withByteBuff(blk.buf.duplicate()) // Duplicate the buffer.
2081+
.withOffset(blk.offset)
2082+
.withOnDiskDataSizeWithHeader(blk.onDiskDataSizeWithHeader)
2083+
.withNextBlockOnDiskSize(blk.nextBlockOnDiskSize)
2084+
.withHFileContext(blk.fileContext)
2085+
.withByteBuffAllocator(blk.allocator);
2086+
}
2087+
2088+
static HFileBlock shallowClone(HFileBlock blk) {
2089+
return createBuilder(blk).build();
2090+
}
2091+
2092+
static HFileBlock deepCloneOnHeap(HFileBlock blk) {
2093+
ByteBuff deepCloned = ByteBuff.wrap(ByteBuffer.wrap(blk.buf.toBytes(0, blk.buf.limit())));
2094+
return createBuilder(blk).withByteBuff(deepCloned).build();
20892095
}
20902096
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.io.hfile;
19+
20+
import static javax.swing.Spring.UNSET;
21+
import static org.apache.hadoop.hbase.io.ByteBuffAllocator.HEAP;
22+
23+
import org.apache.hadoop.hbase.io.ByteBuffAllocator;
24+
import org.apache.hadoop.hbase.nio.ByteBuff;
25+
import org.apache.yetus.audience.InterfaceAudience;
26+
27+
@InterfaceAudience.Private
28+
public class HFileBlockBuilder {
29+
30+
private BlockType blockType;
31+
private int onDiskSizeWithoutHeader;
32+
private int onDiskDataSizeWithHeader;
33+
private int uncompressedSizeWithoutHeader;
34+
private long prevBlockOffset;
35+
private ByteBuff buf;
36+
private boolean fillHeader = false;
37+
private long offset = UNSET;
38+
private int nextBlockOnDiskSize = UNSET;
39+
private HFileContext fileContext;
40+
private ByteBuffAllocator allocator = HEAP;
41+
42+
public HFileBlockBuilder withBlockType(BlockType blockType) {
43+
this.blockType = blockType;
44+
return this;
45+
}
46+
47+
public HFileBlockBuilder withOnDiskSizeWithoutHeader(int onDiskSizeWithoutHeader) {
48+
this.onDiskSizeWithoutHeader = onDiskSizeWithoutHeader;
49+
return this;
50+
}
51+
52+
public HFileBlockBuilder withOnDiskDataSizeWithHeader(int onDiskDataSizeWithHeader) {
53+
this.onDiskDataSizeWithHeader = onDiskDataSizeWithHeader;
54+
return this;
55+
}
56+
57+
public HFileBlockBuilder withUncompressedSizeWithoutHeader(int uncompressedSizeWithoutHeader) {
58+
this.uncompressedSizeWithoutHeader = uncompressedSizeWithoutHeader;
59+
return this;
60+
}
61+
62+
public HFileBlockBuilder withPrevBlockOffset(long prevBlockOffset) {
63+
this.prevBlockOffset = prevBlockOffset;
64+
return this;
65+
}
66+
67+
public HFileBlockBuilder withByteBuff(ByteBuff buf) {
68+
this.buf = buf;
69+
return this;
70+
}
71+
72+
public HFileBlockBuilder withFillHeader(boolean fillHeader) {
73+
this.fillHeader = fillHeader;
74+
return this;
75+
}
76+
77+
public HFileBlockBuilder withOffset(long offset) {
78+
this.offset = offset;
79+
return this;
80+
}
81+
82+
public HFileBlockBuilder withNextBlockOnDiskSize(int nextBlockOnDiskSize) {
83+
this.nextBlockOnDiskSize = nextBlockOnDiskSize;
84+
return this;
85+
}
86+
87+
public HFileBlockBuilder withHFileContext(HFileContext fileContext) {
88+
this.fileContext = fileContext;
89+
return this;
90+
}
91+
92+
public HFileBlockBuilder withByteBuffAllocator(ByteBuffAllocator allocator) {
93+
this.allocator = allocator;
94+
return this;
95+
}
96+
97+
public HFileBlock build() {
98+
if (buf.hasArray()) {
99+
return new HeapHFileBlock(blockType, onDiskSizeWithoutHeader, uncompressedSizeWithoutHeader,
100+
prevBlockOffset, buf, fillHeader, offset, nextBlockOnDiskSize, onDiskDataSizeWithHeader,
101+
fileContext);
102+
} else {
103+
return new HFileBlock(blockType, onDiskSizeWithoutHeader, uncompressedSizeWithoutHeader,
104+
prevBlockOffset, buf, fillHeader, offset, nextBlockOnDiskSize, onDiskDataSizeWithHeader,
105+
fileContext, allocator);
106+
}
107+
}
108+
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -523,15 +523,15 @@ void updateCurrBlockRef(HFileBlock block) {
523523
if (block != null && curBlock != null && block.getOffset() == curBlock.getOffset()) {
524524
return;
525525
}
526-
if (this.curBlock != null) {
526+
if (this.curBlock != null && !this.curBlock.isOnHeap()) {
527527
prevBlocks.add(this.curBlock);
528528
}
529529
this.curBlock = block;
530530
}
531531

532532
void reset() {
533-
// We don't have to keep ref to EXCLUSIVE type of block
534-
if (this.curBlock != null) {
533+
// We don't have to keep ref to heap block
534+
if (this.curBlock != null && !this.curBlock.isOnHeap()) {
535535
this.prevBlocks.add(this.curBlock);
536536
}
537537
this.curBlock = null;

0 commit comments

Comments
 (0)