1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.io.hfile;
21
22 import java.io.DataOutput;
23 import java.io.DataOutputStream;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.fs.FSDataOutputStream;
32 import org.apache.hadoop.fs.FileSystem;
33 import org.apache.hadoop.fs.Path;
34 import org.apache.hadoop.hbase.Cell;
35 import org.apache.hadoop.hbase.CellComparator;
36 import org.apache.hadoop.hbase.CellUtil;
37 import org.apache.hadoop.hbase.KeyValue.KVComparator;
38 import org.apache.hadoop.hbase.classification.InterfaceAudience;
39 import org.apache.hadoop.hbase.io.hfile.HFile.Writer;
40 import org.apache.hadoop.hbase.io.hfile.HFileBlock.BlockWritable;
41 import org.apache.hadoop.hbase.util.BloomFilterWriter;
42 import org.apache.hadoop.hbase.util.Bytes;
43 import org.apache.hadoop.io.Writable;
44
45
46
47
48 @InterfaceAudience.Private
49 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD",
50 justification="Understood but doing it anyway; HBASE-14730")
51 public class HFileWriterV2 extends AbstractHFileWriter {
52 static final Log LOG = LogFactory.getLog(HFileWriterV2.class);
53
54
55 public static final byte [] MAX_MEMSTORE_TS_KEY =
56 Bytes.toBytes("MAX_MEMSTORE_TS_KEY");
57
58
59 public static final byte [] KEY_VALUE_VERSION =
60 Bytes.toBytes("KEY_VALUE_VERSION");
61
62
63 public static final int KEY_VALUE_VER_WITH_MEMSTORE = 1;
64
65
66 private List<InlineBlockWriter> inlineBlockWriters =
67 new ArrayList<InlineBlockWriter>();
68
69
70 protected HFileBlock.Writer fsBlockWriter;
71
72 private HFileBlockIndex.BlockIndexWriter dataBlockIndexWriter;
73 private HFileBlockIndex.BlockIndexWriter metaBlockIndexWriter;
74
75
76 private long firstDataBlockOffset = -1;
77
78
79 protected long lastDataBlockOffset;
80
81
82
83
84
85 private Cell lastCellOfPreviousBlock = null;
86
87
88 private List<BlockWritable> additionalLoadOnOpenData =
89 new ArrayList<BlockWritable>();
90
91 protected long maxMemstoreTS = 0;
92
93
94 private static boolean warnCellWithTags = true;
95
96
97
98 public static final String UNIFIED_ENCODED_BLOCKSIZE_RATIO = "hbase.writer.unified.encoded.blocksize.ratio";
99
100
101 private final int encodedBlockSizeLimit;
102
103
104 static class WriterFactoryV2 extends HFile.WriterFactory {
105 WriterFactoryV2(Configuration conf, CacheConfig cacheConf) {
106 super(conf, cacheConf);
107 }
108
109 @Override
110 public Writer createWriter(FileSystem fs, Path path,
111 FSDataOutputStream ostream,
112 KVComparator comparator, HFileContext context) throws IOException {
113 context.setIncludesTags(false);
114 return new HFileWriterV2(conf, cacheConf, fs, path, ostream,
115 comparator, context);
116 }
117 }
118
119
120 public HFileWriterV2(Configuration conf, CacheConfig cacheConf,
121 FileSystem fs, Path path, FSDataOutputStream ostream,
122 final KVComparator comparator, final HFileContext context) throws IOException {
123 super(cacheConf,
124 ostream == null ? createOutputStream(conf, fs, path, null) : ostream,
125 path, comparator, context);
126 float encodeBlockSizeRatio = conf.getFloat(UNIFIED_ENCODED_BLOCKSIZE_RATIO, 1f);
127 this.encodedBlockSizeLimit = (int)(hFileContext.getBlocksize() * encodeBlockSizeRatio);
128 finishInit(conf);
129 }
130
131
132 protected void finishInit(final Configuration conf) {
133 if (fsBlockWriter != null)
134 throw new IllegalStateException("finishInit called twice");
135
136 fsBlockWriter = new HFileBlock.Writer(blockEncoder, hFileContext);
137
138
139 boolean cacheIndexesOnWrite = cacheConf.shouldCacheIndexesOnWrite();
140 dataBlockIndexWriter = new HFileBlockIndex.BlockIndexWriter(fsBlockWriter,
141 cacheIndexesOnWrite ? cacheConf : null,
142 cacheIndexesOnWrite ? name : null);
143 dataBlockIndexWriter.setMaxChunkSize(
144 HFileBlockIndex.getMaxChunkSize(conf));
145 dataBlockIndexWriter.setMinIndexNumEntries(
146 HFileBlockIndex.getMinIndexNumEntries(conf));
147 inlineBlockWriters.add(dataBlockIndexWriter);
148
149
150 metaBlockIndexWriter = new HFileBlockIndex.BlockIndexWriter();
151 if (LOG.isTraceEnabled()) LOG.trace("Initialized with " + cacheConf);
152 }
153
154
155
156
157
158
159 protected void checkBlockBoundary() throws IOException {
160
161
162 if (fsBlockWriter.encodedBlockSizeWritten() >= encodedBlockSizeLimit
163 || fsBlockWriter.blockSizeWritten() >= hFileContext.getBlocksize()) {
164 finishBlock();
165 writeInlineBlocks(false);
166 newBlock();
167 }
168 }
169
170
171 private void finishBlock() throws IOException {
172 if (!fsBlockWriter.isWriting() || fsBlockWriter.blockSizeWritten() == 0)
173 return;
174
175
176 if (firstDataBlockOffset == -1) {
177 firstDataBlockOffset = outputStream.getPos();
178 }
179
180 lastDataBlockOffset = outputStream.getPos();
181 fsBlockWriter.writeHeaderAndData(outputStream);
182 int onDiskSize = fsBlockWriter.getOnDiskSizeWithHeader();
183
184 Cell indexEntry =
185 CellComparator.getMidpoint(this.comparator, lastCellOfPreviousBlock, firstCellInBlock);
186 dataBlockIndexWriter.addEntry(CellUtil.getCellKeySerializedAsKeyValueKey(indexEntry),
187 lastDataBlockOffset, onDiskSize);
188 totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();
189 if (cacheConf.shouldCacheDataOnWrite()) {
190 doCacheOnWrite(lastDataBlockOffset);
191 }
192 }
193
194
195 private void writeInlineBlocks(boolean closing) throws IOException {
196 for (InlineBlockWriter ibw : inlineBlockWriters) {
197 while (ibw.shouldWriteBlock(closing)) {
198 long offset = outputStream.getPos();
199 boolean cacheThisBlock = ibw.getCacheOnWrite();
200 ibw.writeInlineBlock(fsBlockWriter.startWriting(
201 ibw.getInlineBlockType()));
202 fsBlockWriter.writeHeaderAndData(outputStream);
203 ibw.blockWritten(offset, fsBlockWriter.getOnDiskSizeWithHeader(),
204 fsBlockWriter.getUncompressedSizeWithoutHeader());
205 totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();
206
207 if (cacheThisBlock) {
208 doCacheOnWrite(offset);
209 }
210 }
211 }
212 }
213
214
215
216
217
218
219 private void doCacheOnWrite(long offset) {
220 HFileBlock cacheFormatBlock = fsBlockWriter.getBlockForCaching(cacheConf);
221 cacheConf.getBlockCache().cacheBlock(
222 new BlockCacheKey(name, offset, true, cacheFormatBlock.getBlockType()), cacheFormatBlock);
223 }
224
225
226
227
228
229
230 protected void newBlock() throws IOException {
231
232 fsBlockWriter.startWriting(BlockType.DATA);
233 firstCellInBlock = null;
234 if (lastCell != null) {
235 lastCellOfPreviousBlock = lastCell;
236 }
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250 @Override
251 public void appendMetaBlock(String metaBlockName, Writable content) {
252 byte[] key = Bytes.toBytes(metaBlockName);
253 int i;
254 for (i = 0; i < metaNames.size(); ++i) {
255
256 byte[] cur = metaNames.get(i);
257 if (Bytes.BYTES_RAWCOMPARATOR.compare(cur, 0, cur.length, key, 0,
258 key.length) > 0) {
259 break;
260 }
261 }
262 metaNames.add(i, key);
263 metaData.add(i, content);
264 }
265
266
267
268
269
270
271
272
273 @Override
274 public void append(final Cell cell) throws IOException {
275 byte[] value = cell.getValueArray();
276 int voffset = cell.getValueOffset();
277 int vlength = cell.getValueLength();
278
279 boolean dupKey = checkKey(cell);
280 checkValue(value, voffset, vlength);
281 if (!dupKey) {
282 checkBlockBoundary();
283 }
284
285 if (!fsBlockWriter.isWriting()) {
286 newBlock();
287 }
288
289 if (warnCellWithTags && getFileContext().isIncludesTags()) {
290 LOG.warn("A minimum HFile version of " + HFile.MIN_FORMAT_VERSION_WITH_TAGS
291 + " is required to support cell attributes/tags. Consider setting "
292 + HFile.FORMAT_VERSION_KEY + " accordingly.");
293 warnCellWithTags = false;
294 }
295
296 fsBlockWriter.write(cell);
297
298 totalKeyLength += CellUtil.estimatedSerializedSizeOfKey(cell);
299 totalValueLength += vlength;
300
301
302 if (firstCellInBlock == null) {
303
304
305 firstCellInBlock = cell;
306 }
307
308
309
310 lastCell = cell;
311 entryCount++;
312 this.maxMemstoreTS = Math.max(this.maxMemstoreTS, cell.getSequenceId());
313 }
314
315 @Override
316 public void close() throws IOException {
317 if (outputStream == null) {
318 return;
319 }
320
321 blockEncoder.saveMetadata(this);
322
323
324
325 finishBlock();
326 writeInlineBlocks(true);
327
328 FixedFileTrailer trailer = new FixedFileTrailer(getMajorVersion(), getMinorVersion());
329
330
331 if (!metaNames.isEmpty()) {
332 for (int i = 0; i < metaNames.size(); ++i) {
333
334 long offset = outputStream.getPos();
335
336 DataOutputStream dos = fsBlockWriter.startWriting(BlockType.META);
337 metaData.get(i).write(dos);
338
339 fsBlockWriter.writeHeaderAndData(outputStream);
340 totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();
341
342
343 metaBlockIndexWriter.addEntry(metaNames.get(i), offset,
344 fsBlockWriter.getOnDiskSizeWithHeader());
345 }
346 }
347
348
349
350
351
352
353
354
355
356
357 long rootIndexOffset = dataBlockIndexWriter.writeIndexBlocks(outputStream);
358 trailer.setLoadOnOpenOffset(rootIndexOffset);
359
360
361 metaBlockIndexWriter.writeSingleLevelIndex(fsBlockWriter.startWriting(
362 BlockType.ROOT_INDEX), "meta");
363 fsBlockWriter.writeHeaderAndData(outputStream);
364 totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();
365
366 if (this.hFileContext.isIncludesMvcc()) {
367 appendFileInfo(MAX_MEMSTORE_TS_KEY, Bytes.toBytes(maxMemstoreTS));
368 appendFileInfo(KEY_VALUE_VERSION, Bytes.toBytes(KEY_VALUE_VER_WITH_MEMSTORE));
369 }
370
371
372 writeFileInfo(trailer, fsBlockWriter.startWriting(BlockType.FILE_INFO));
373 fsBlockWriter.writeHeaderAndData(outputStream);
374 totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();
375
376
377 for (BlockWritable w : additionalLoadOnOpenData){
378 fsBlockWriter.writeBlock(w, outputStream);
379 totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();
380 }
381
382
383 trailer.setNumDataIndexLevels(dataBlockIndexWriter.getNumLevels());
384 trailer.setUncompressedDataIndexSize(
385 dataBlockIndexWriter.getTotalUncompressedSize());
386 trailer.setFirstDataBlockOffset(firstDataBlockOffset);
387 trailer.setLastDataBlockOffset(lastDataBlockOffset);
388 trailer.setComparatorClass(comparator.getClass());
389 trailer.setDataIndexCount(dataBlockIndexWriter.getNumRootEntries());
390
391
392 finishClose(trailer);
393
394 fsBlockWriter.release();
395 }
396
397 @Override
398 public void addInlineBlockWriter(InlineBlockWriter ibw) {
399 inlineBlockWriters.add(ibw);
400 }
401
402 @Override
403 public void addGeneralBloomFilter(final BloomFilterWriter bfw) {
404 this.addBloomFilter(bfw, BlockType.GENERAL_BLOOM_META);
405 }
406
407 @Override
408 public void addDeleteFamilyBloomFilter(final BloomFilterWriter bfw) {
409 this.addBloomFilter(bfw, BlockType.DELETE_FAMILY_BLOOM_META);
410 }
411
412 private void addBloomFilter(final BloomFilterWriter bfw,
413 final BlockType blockType) {
414 if (bfw.getKeyCount() <= 0)
415 return;
416
417 if (blockType != BlockType.GENERAL_BLOOM_META &&
418 blockType != BlockType.DELETE_FAMILY_BLOOM_META) {
419 throw new RuntimeException("Block Type: " + blockType.toString() +
420 "is not supported");
421 }
422 additionalLoadOnOpenData.add(new BlockWritable() {
423 @Override
424 public BlockType getBlockType() {
425 return blockType;
426 }
427
428 @Override
429 public void writeToBlock(DataOutput out) throws IOException {
430 bfw.getMetaWriter().write(out);
431 Writable dataWriter = bfw.getDataWriter();
432 if (dataWriter != null)
433 dataWriter.write(out);
434 }
435 });
436 }
437
438 protected int getMajorVersion() {
439 return 2;
440 }
441
442 protected int getMinorVersion() {
443 return HFileReaderV2.MAX_MINOR_VERSION;
444 }
445
446 @Override
447 public HFileContext getFileContext() {
448 return hFileContext;
449 }
450 }