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