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.wal; 019 020import java.io.IOException; 021import java.io.InterruptedIOException; 022import java.io.OutputStream; 023import java.nio.ByteBuffer; 024import java.util.concurrent.CompletableFuture; 025import java.util.concurrent.ExecutionException; 026import java.util.function.Consumer; 027import org.apache.hadoop.fs.FileSystem; 028import org.apache.hadoop.fs.Path; 029import org.apache.hadoop.hbase.Cell; 030import org.apache.hadoop.hbase.io.ByteBufferWriter; 031import org.apache.hadoop.hbase.io.asyncfs.AsyncFSOutput; 032import org.apache.hadoop.hbase.io.asyncfs.AsyncFSOutputHelper; 033import org.apache.hadoop.hbase.util.CommonFSUtils.StreamLacksCapabilityException; 034import org.apache.hadoop.hbase.wal.AsyncFSWALProvider; 035import org.apache.hadoop.hbase.wal.WAL.Entry; 036import org.apache.yetus.audience.InterfaceAudience; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040import org.apache.hbase.thirdparty.com.google.common.base.Throwables; 041import org.apache.hbase.thirdparty.io.netty.channel.Channel; 042import org.apache.hbase.thirdparty.io.netty.channel.EventLoopGroup; 043 044import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.WALHeader; 045import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos.WALTrailer; 046 047/** 048 * AsyncWriter for protobuf-based WAL. 049 */ 050@InterfaceAudience.Private 051public class AsyncProtobufLogWriter extends AbstractProtobufLogWriter 052 implements AsyncFSWALProvider.AsyncWriter { 053 054 private static final Logger LOG = LoggerFactory.getLogger(AsyncProtobufLogWriter.class); 055 056 private final EventLoopGroup eventLoopGroup; 057 058 private final Class<? extends Channel> channelClass; 059 060 private AsyncFSOutput output; 061 062 private static final class OutputStreamWrapper extends OutputStream 063 implements ByteBufferWriter { 064 065 private final AsyncFSOutput out; 066 067 private final byte[] oneByteBuf = new byte[1]; 068 069 @Override 070 public void write(int b) throws IOException { 071 oneByteBuf[0] = (byte) b; 072 write(oneByteBuf); 073 } 074 075 public OutputStreamWrapper(AsyncFSOutput out) { 076 this.out = out; 077 } 078 079 @Override 080 public void write(ByteBuffer b, int off, int len) throws IOException { 081 ByteBuffer bb = b.duplicate(); 082 bb.position(off); 083 bb.limit(off + len); 084 out.write(bb); 085 } 086 087 @Override 088 public void writeInt(int i) throws IOException { 089 out.writeInt(i); 090 } 091 092 @Override 093 public void write(byte[] b, int off, int len) throws IOException { 094 out.write(b, off, len); 095 } 096 097 @Override 098 public void close() throws IOException { 099 out.close(); 100 } 101 } 102 103 private OutputStream asyncOutputWrapper; 104 105 public AsyncProtobufLogWriter(EventLoopGroup eventLoopGroup, 106 Class<? extends Channel> channelClass) { 107 this.eventLoopGroup = eventLoopGroup; 108 this.channelClass = channelClass; 109 } 110 111 /* 112 * @return class name which is recognized by hbase-1.x to avoid ProtobufLogReader throwing error: 113 * IOException: Got unknown writer class: AsyncProtobufLogWriter 114 */ 115 @Override 116 protected String getWriterClassName() { 117 return "ProtobufLogWriter"; 118 } 119 120 @Override 121 public void append(Entry entry) { 122 int buffered = output.buffered(); 123 entry.setCompressionContext(compressionContext); 124 try { 125 entry.getKey(). 126 getBuilder(compressor).setFollowingKvCount(entry.getEdit().size()).build() 127 .writeDelimitedTo(asyncOutputWrapper); 128 } catch (IOException e) { 129 throw new AssertionError("should not happen", e); 130 } 131 try { 132 for (Cell cell : entry.getEdit().getCells()) { 133 cellEncoder.write(cell); 134 } 135 } catch (IOException e) { 136 throw new AssertionError("should not happen", e); 137 } 138 length.addAndGet(output.buffered() - buffered); 139 } 140 141 @Override 142 public CompletableFuture<Long> sync() { 143 return output.flush(false); 144 } 145 146 @Override 147 public synchronized void close() throws IOException { 148 if (this.output == null) { 149 return; 150 } 151 try { 152 writeWALTrailer(); 153 output.close(); 154 } catch (Exception e) { 155 LOG.warn("normal close failed, try recover", e); 156 output.recoverAndClose(null); 157 } 158 this.output = null; 159 } 160 161 public AsyncFSOutput getOutput() { 162 return this.output; 163 } 164 165 @Override 166 protected void initOutput(FileSystem fs, Path path, boolean overwritable, int bufferSize, 167 short replication, long blockSize) throws IOException, StreamLacksCapabilityException { 168 this.output = AsyncFSOutputHelper.createOutput(fs, path, overwritable, false, replication, 169 blockSize, eventLoopGroup, channelClass); 170 this.asyncOutputWrapper = new OutputStreamWrapper(output); 171 } 172 173 private long write(Consumer<CompletableFuture<Long>> action) throws IOException { 174 CompletableFuture<Long> future = new CompletableFuture<>(); 175 action.accept(future); 176 try { 177 return future.get().longValue(); 178 } catch (InterruptedException e) { 179 InterruptedIOException ioe = new InterruptedIOException(); 180 ioe.initCause(e); 181 throw ioe; 182 } catch (ExecutionException e) { 183 Throwables.propagateIfPossible(e.getCause(), IOException.class); 184 throw new RuntimeException(e.getCause()); 185 } 186 } 187 188 @Override 189 protected long writeMagicAndWALHeader(byte[] magic, WALHeader header) throws IOException { 190 return write(future -> { 191 output.write(magic); 192 try { 193 header.writeDelimitedTo(asyncOutputWrapper); 194 } catch (IOException e) { 195 // should not happen 196 throw new AssertionError(e); 197 } 198 output.flush(false).whenComplete((len, error) -> { 199 if (error != null) { 200 future.completeExceptionally(error); 201 } else { 202 future.complete(len); 203 } 204 }); 205 }); 206 } 207 208 @Override 209 protected long writeWALTrailerAndMagic(WALTrailer trailer, byte[] magic) throws IOException { 210 return write(future -> { 211 try { 212 trailer.writeTo(asyncOutputWrapper); 213 } catch (IOException e) { 214 // should not happen 215 throw new AssertionError(e); 216 } 217 output.writeInt(trailer.getSerializedSize()); 218 output.write(magic); 219 output.flush(false).whenComplete((len, error) -> { 220 if (error != null) { 221 future.completeExceptionally(error); 222 } else { 223 future.complete(len); 224 } 225 }); 226 }); 227 } 228 229 @Override 230 protected OutputStream getOutputStreamForCellEncoder() { 231 return asyncOutputWrapper; 232 } 233}