001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.regionserver; 019 020import java.io.IOException; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.List; 024 025import org.apache.hadoop.fs.Path; 026import org.apache.yetus.audience.InterfaceAudience; 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029import org.apache.hadoop.hbase.regionserver.CellSink; 030 031/** 032 * Base class for cell sink that separates the provided cells into multiple files. 033 */ 034@InterfaceAudience.Private 035public abstract class AbstractMultiFileWriter implements CellSink, ShipperListener { 036 037 private static final Logger LOG = LoggerFactory.getLogger(AbstractMultiFileWriter.class); 038 039 /** Factory that is used to produce single StoreFile.Writer-s */ 040 protected WriterFactory writerFactory; 041 042 /** Source scanner that is tracking KV count; may be null if source is not StoreScanner */ 043 protected StoreScanner sourceScanner; 044 045 public interface WriterFactory { 046 public StoreFileWriter createWriter() throws IOException; 047 } 048 049 /** 050 * Initializes multi-writer before usage. 051 * @param sourceScanner Optional store scanner to obtain the information about read progress. 052 * @param factory Factory used to produce individual file writers. 053 */ 054 public void init(StoreScanner sourceScanner, WriterFactory factory) { 055 this.writerFactory = factory; 056 this.sourceScanner = sourceScanner; 057 } 058 059 /** 060 * Commit all writers. 061 * <p> 062 * Notice that here we use the same <code>maxSeqId</code> for all output files since we haven't 063 * find an easy to find enough sequence ids for different output files in some corner cases. See 064 * comments in HBASE-15400 for more details. 065 */ 066 public List<Path> commitWriters(long maxSeqId, boolean majorCompaction) throws IOException { 067 preCommitWriters(); 068 Collection<StoreFileWriter> writers = this.writers(); 069 if (LOG.isDebugEnabled()) { 070 LOG.debug("Commit " + writers.size() + " writers, maxSeqId=" + maxSeqId 071 + ", majorCompaction=" + majorCompaction); 072 } 073 List<Path> paths = new ArrayList<>(); 074 for (StoreFileWriter writer : writers) { 075 if (writer == null) { 076 continue; 077 } 078 writer.appendMetadata(maxSeqId, majorCompaction); 079 preCloseWriter(writer); 080 paths.add(writer.getPath()); 081 writer.close(); 082 } 083 return paths; 084 } 085 086 /** 087 * Close all writers without throwing any exceptions. This is used when compaction failed usually. 088 */ 089 public List<Path> abortWriters() { 090 List<Path> paths = new ArrayList<>(); 091 for (StoreFileWriter writer : writers()) { 092 try { 093 if (writer != null) { 094 paths.add(writer.getPath()); 095 writer.close(); 096 } 097 } catch (Exception ex) { 098 LOG.error("Failed to close the writer after an unfinished compaction.", ex); 099 } 100 } 101 return paths; 102 } 103 104 protected abstract Collection<StoreFileWriter> writers(); 105 106 /** 107 * Subclasses override this method to be called at the end of a successful sequence of append; all 108 * appends are processed before this method is called. 109 */ 110 protected void preCommitWriters() throws IOException { 111 } 112 113 /** 114 * Subclasses override this method to be called before we close the give writer. Usually you can 115 * append extra metadata to the writer. 116 */ 117 protected void preCloseWriter(StoreFileWriter writer) throws IOException { 118 } 119 120 @Override 121 public void beforeShipped() throws IOException { 122 if (this.writers() != null) { 123 for (StoreFileWriter writer : writers()) { 124 writer.beforeShipped(); 125 } 126 } 127 } 128}