|
35 | 35 | import org.apache.hadoop.hbase.CellComparator;
|
36 | 36 | import org.apache.hadoop.hbase.CellUtil;
|
37 | 37 | import org.apache.hadoop.hbase.ExtendedCell;
|
| 38 | +import org.apache.hadoop.hbase.HBaseInterfaceAudience; |
38 | 39 | import org.apache.hadoop.hbase.HConstants;
|
39 | 40 | import org.apache.hadoop.hbase.KeyValue;
|
40 | 41 | import org.apache.hadoop.hbase.PrivateCellUtil;
|
|
46 | 47 | import org.apache.hadoop.hbase.io.encoding.DataBlockEncoder;
|
47 | 48 | import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
|
48 | 49 | import org.apache.hadoop.hbase.io.encoding.HFileBlockDecodingContext;
|
| 50 | +import org.apache.hadoop.hbase.monitoring.ThreadLocalServerSideScanMetrics; |
49 | 51 | import org.apache.hadoop.hbase.nio.ByteBuff;
|
50 | 52 | import org.apache.hadoop.hbase.regionserver.KeyValueScanner;
|
51 | 53 | import org.apache.hadoop.hbase.util.ByteBufferUtils;
|
@@ -1097,71 +1099,91 @@ public void setConf(Configuration conf) {
|
1097 | 1099 | * Retrieve block from cache. Validates the retrieved block's type vs {@code expectedBlockType}
|
1098 | 1100 | * and its encoding vs. {@code expectedDataBlockEncoding}. Unpacks the block as necessary.
|
1099 | 1101 | */
|
1100 |
| - private HFileBlock getCachedBlock(BlockCacheKey cacheKey, boolean cacheBlock, boolean useLock, |
| 1102 | + @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.UNITTEST) |
| 1103 | + public HFileBlock getCachedBlock(BlockCacheKey cacheKey, boolean cacheBlock, boolean useLock, |
1101 | 1104 | boolean updateCacheMetrics, BlockType expectedBlockType,
|
1102 | 1105 | DataBlockEncoding expectedDataBlockEncoding) throws IOException {
|
1103 | 1106 | // Check cache for block. If found return.
|
1104 | 1107 | BlockCache cache = cacheConf.getBlockCache().orElse(null);
|
| 1108 | + long cachedBlockBytesRead = 0; |
1105 | 1109 | if (cache != null) {
|
1106 |
| - HFileBlock cachedBlock = (HFileBlock) cache.getBlock(cacheKey, cacheBlock, useLock, |
1107 |
| - updateCacheMetrics, expectedBlockType); |
1108 |
| - if (cachedBlock != null) { |
1109 |
| - if (cacheConf.shouldCacheCompressed(cachedBlock.getBlockType().getCategory())) { |
1110 |
| - HFileBlock compressedBlock = cachedBlock; |
1111 |
| - cachedBlock = compressedBlock.unpack(hfileContext, fsBlockReader); |
1112 |
| - // In case of compressed block after unpacking we can release the compressed block |
1113 |
| - if (compressedBlock != cachedBlock) { |
1114 |
| - compressedBlock.release(); |
| 1110 | + HFileBlock cachedBlock = null; |
| 1111 | + boolean isScanMetricsEnabled = ThreadLocalServerSideScanMetrics.isScanMetricsEnabled(); |
| 1112 | + try { |
| 1113 | + cachedBlock = (HFileBlock) cache.getBlock(cacheKey, cacheBlock, useLock, updateCacheMetrics, |
| 1114 | + expectedBlockType); |
| 1115 | + if (cachedBlock != null) { |
| 1116 | + if (cacheConf.shouldCacheCompressed(cachedBlock.getBlockType().getCategory())) { |
| 1117 | + HFileBlock compressedBlock = cachedBlock; |
| 1118 | + cachedBlock = compressedBlock.unpack(hfileContext, fsBlockReader); |
| 1119 | + // In case of compressed block after unpacking we can release the compressed block |
| 1120 | + if (compressedBlock != cachedBlock) { |
| 1121 | + compressedBlock.release(); |
| 1122 | + } |
| 1123 | + } |
| 1124 | + try { |
| 1125 | + validateBlockType(cachedBlock, expectedBlockType); |
| 1126 | + } catch (IOException e) { |
| 1127 | + returnAndEvictBlock(cache, cacheKey, cachedBlock); |
| 1128 | + cachedBlock = null; |
| 1129 | + throw e; |
1115 | 1130 | }
|
1116 |
| - } |
1117 |
| - try { |
1118 |
| - validateBlockType(cachedBlock, expectedBlockType); |
1119 |
| - } catch (IOException e) { |
1120 |
| - returnAndEvictBlock(cache, cacheKey, cachedBlock); |
1121 |
| - throw e; |
1122 |
| - } |
1123 | 1131 |
|
1124 |
| - if (expectedDataBlockEncoding == null) { |
1125 |
| - return cachedBlock; |
1126 |
| - } |
1127 |
| - DataBlockEncoding actualDataBlockEncoding = cachedBlock.getDataBlockEncoding(); |
1128 |
| - // Block types other than data blocks always have |
1129 |
| - // DataBlockEncoding.NONE. To avoid false negative cache misses, only |
1130 |
| - // perform this check if cached block is a data block. |
1131 |
| - if ( |
1132 |
| - cachedBlock.getBlockType().isData() |
1133 |
| - && !actualDataBlockEncoding.equals(expectedDataBlockEncoding) |
1134 |
| - ) { |
1135 |
| - // This mismatch may happen if a Scanner, which is used for say a |
1136 |
| - // compaction, tries to read an encoded block from the block cache. |
1137 |
| - // The reverse might happen when an EncodedScanner tries to read |
1138 |
| - // un-encoded blocks which were cached earlier. |
1139 |
| - // |
1140 |
| - // Because returning a data block with an implicit BlockType mismatch |
1141 |
| - // will cause the requesting scanner to throw a disk read should be |
1142 |
| - // forced here. This will potentially cause a significant number of |
1143 |
| - // cache misses, so update so we should keep track of this as it might |
1144 |
| - // justify the work on a CompoundScanner. |
| 1132 | + if (expectedDataBlockEncoding == null) { |
| 1133 | + return cachedBlock; |
| 1134 | + } |
| 1135 | + DataBlockEncoding actualDataBlockEncoding = cachedBlock.getDataBlockEncoding(); |
| 1136 | + // Block types other than data blocks always have |
| 1137 | + // DataBlockEncoding.NONE. To avoid false negative cache misses, only |
| 1138 | + // perform this check if cached block is a data block. |
1145 | 1139 | if (
|
1146 |
| - !expectedDataBlockEncoding.equals(DataBlockEncoding.NONE) |
1147 |
| - && !actualDataBlockEncoding.equals(DataBlockEncoding.NONE) |
| 1140 | + cachedBlock.getBlockType().isData() |
| 1141 | + && !actualDataBlockEncoding.equals(expectedDataBlockEncoding) |
1148 | 1142 | ) {
|
1149 |
| - // If the block is encoded but the encoding does not match the |
1150 |
| - // expected encoding it is likely the encoding was changed but the |
1151 |
| - // block was not yet evicted. Evictions on file close happen async |
1152 |
| - // so blocks with the old encoding still linger in cache for some |
1153 |
| - // period of time. This event should be rare as it only happens on |
1154 |
| - // schema definition change. |
1155 |
| - LOG.info( |
1156 |
| - "Evicting cached block with key {} because data block encoding mismatch; " |
1157 |
| - + "expected {}, actual {}, path={}", |
1158 |
| - cacheKey, actualDataBlockEncoding, expectedDataBlockEncoding, path); |
1159 |
| - // This is an error scenario. so here we need to release the block. |
1160 |
| - returnAndEvictBlock(cache, cacheKey, cachedBlock); |
| 1143 | + // This mismatch may happen if a Scanner, which is used for say a |
| 1144 | + // compaction, tries to read an encoded block from the block cache. |
| 1145 | + // The reverse might happen when an EncodedScanner tries to read |
| 1146 | + // un-encoded blocks which were cached earlier. |
| 1147 | + // |
| 1148 | + // Because returning a data block with an implicit BlockType mismatch |
| 1149 | + // will cause the requesting scanner to throw a disk read should be |
| 1150 | + // forced here. This will potentially cause a significant number of |
| 1151 | + // cache misses, so update so we should keep track of this as it might |
| 1152 | + // justify the work on a CompoundScanner. |
| 1153 | + if ( |
| 1154 | + !expectedDataBlockEncoding.equals(DataBlockEncoding.NONE) |
| 1155 | + && !actualDataBlockEncoding.equals(DataBlockEncoding.NONE) |
| 1156 | + ) { |
| 1157 | + // If the block is encoded but the encoding does not match the |
| 1158 | + // expected encoding it is likely the encoding was changed but the |
| 1159 | + // block was not yet evicted. Evictions on file close happen async |
| 1160 | + // so blocks with the old encoding still linger in cache for some |
| 1161 | + // period of time. This event should be rare as it only happens on |
| 1162 | + // schema definition change. |
| 1163 | + LOG.info( |
| 1164 | + "Evicting cached block with key {} because data block encoding mismatch; " |
| 1165 | + + "expected {}, actual {}, path={}", |
| 1166 | + cacheKey, actualDataBlockEncoding, expectedDataBlockEncoding, path); |
| 1167 | + // This is an error scenario. so here we need to release the block. |
| 1168 | + returnAndEvictBlock(cache, cacheKey, cachedBlock); |
| 1169 | + } |
| 1170 | + cachedBlock = null; |
| 1171 | + return null; |
1161 | 1172 | }
|
1162 |
| - return null; |
| 1173 | + return cachedBlock; |
| 1174 | + } |
| 1175 | + } finally { |
| 1176 | + // Count bytes read as cached block is being returned |
| 1177 | + if (isScanMetricsEnabled && cachedBlock != null) { |
| 1178 | + cachedBlockBytesRead = cachedBlock.getOnDiskSizeWithHeader(); |
| 1179 | + // Account for the header size of the next block if it exists |
| 1180 | + if (cachedBlock.getNextBlockOnDiskSize() > 0) { |
| 1181 | + cachedBlockBytesRead += cachedBlock.headerSize(); |
| 1182 | + } |
| 1183 | + } |
| 1184 | + if (cachedBlockBytesRead > 0) { |
| 1185 | + ThreadLocalServerSideScanMetrics.addBytesReadFromBlockCache(cachedBlockBytesRead); |
1163 | 1186 | }
|
1164 |
| - return cachedBlock; |
1165 | 1187 | }
|
1166 | 1188 | }
|
1167 | 1189 | return null;
|
|
0 commit comments