package com.neverwinterdp.scribengin.storage.hdfs.sink; import java.io.IOException; import java.util.UUID; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import com.neverwinterdp.scribengin.Record; import com.neverwinterdp.scribengin.storage.sink.SinkStreamWriter; import com.neverwinterdp.util.JSONSerializer; import com.neverwinterdp.vm.environment.yarn.HDFSUtil; public class HDFSSinkStreamWriter implements SinkStreamWriter { private FileSystem fs; private String location ; private String bufferLocation ; private SinkBuffer currentBuffer ; public HDFSSinkStreamWriter(FileSystem fs, String location) throws IOException { this.fs = fs; this.location = location; this.bufferLocation = location + "/.buffer" + UUID.randomUUID().toString(); Path bufferPath = new Path(bufferLocation); if(!fs.exists(bufferPath)) fs.mkdirs(bufferPath); this.currentBuffer = nextSinkBuffer(); } @Override synchronized public void append(Record record) throws Exception { currentBuffer.append(record); } @Override public void rollback() throws Exception { currentBuffer.rollback(); } @Override public void prepareCommit() throws Exception { //TODO: reimplement correctly 2 phases commit } @Override public void completeCommit() throws Exception { //TODO: reimplement correctly 2 phases commit currentBuffer.commit(); currentBuffer = nextSinkBuffer(); } @Override synchronized public void commit() throws Exception { try { prepareCommit(); completeCommit(); } catch(Exception ex) { rollback(); throw ex; } } @Override synchronized public void close() throws Exception { if(currentBuffer.count > 0) { currentBuffer.commit(); } else { currentBuffer.delete(); } Path datDestination = new Path(location + "/data-" + UUID.randomUUID().toString() + ".dat"); FileStatus[] status = fs.listStatus(new Path(bufferLocation)); if(status.length == 0) { fs.delete(new Path(this.bufferLocation), true); } else { Path[] bufferSrc = new Path[status.length]; for(int i = 0; i < bufferSrc.length; i++) { bufferSrc[i] = status[i].getPath(); } HDFSUtil.concat(fs, datDestination, bufferSrc, true); fs.delete(new Path(this.bufferLocation), true); } } private SinkBuffer nextSinkBuffer() throws IOException { int idx = 0; if(currentBuffer != null) { idx = currentBuffer.index + 1 ; } SinkBuffer buffer = new SinkBuffer(idx) ; return buffer; } class SinkBuffer { private int index; private Path writingPath; private Path completePath; private FSDataOutputStream output; private int count = 0 ; public SinkBuffer(int index) throws IOException { this.index = index; writingPath = new Path(bufferLocation + "/buffer-" + index + ".writing") ; completePath = new Path(bufferLocation + "/buffer-" + index + ".complete") ; output = fs.create(writingPath) ; } public void append(Record record) throws IOException { byte[] bytes = JSONSerializer.INSTANCE.toBytes(record) ; output.writeInt(bytes.length); output.write(bytes); count++; } public void delete() throws IOException { output.close(); fs.delete(writingPath, true) ; } public void rollback() throws IOException { output.close(); output = fs.create(writingPath, true) ; count = 0; } public void commit() throws IOException { output.close(); fs.rename(writingPath, completePath); } } }