package com.alibaba.doris.dataserver.store.log.db.impl;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.doris.dataserver.store.log.LogStorageException;
import com.alibaba.doris.dataserver.store.log.db.ClumpConfigure;
import com.alibaba.doris.dataserver.store.log.db.LogClumpHead;
import com.alibaba.doris.dataserver.store.log.db.LogFile;
import com.alibaba.doris.dataserver.store.log.db.WriteWindow;
import com.alibaba.doris.dataserver.store.log.db.WriteWindowClosedException;
import com.alibaba.doris.dataserver.store.log.db.LogFile.AccessMode;
import com.alibaba.doris.dataserver.store.log.entry.ClumpHeadEntry;
import com.alibaba.doris.dataserver.store.log.entry.LogEntry;
import com.alibaba.doris.dataserver.store.log.serialize.LogSerializer;
import com.alibaba.doris.dataserver.store.log.serialize.LogSerializerFactory;
import com.alibaba.doris.dataserver.store.log.utils.LogFileUtil;
/**
* @author ajun Email:jack.yuj@alibaba-inc.com
*/
public class DefaultWriteWindowImpl implements WriteWindow {
public DefaultWriteWindowImpl(ClumpConfigure config, String clumpName) {
this(config, clumpName, null);
}
public DefaultWriteWindowImpl(ClumpConfigure config, String clumpName, ByteBuffer buffer) {
this.config = config;
this.clumpName = clumpName;
this.writeBuffer = buffer;
prepare();
}
public synchronized long size() {
checkIsOpen();
return logData.length();
}
private void prepare() {
head = new DefaultLogClumpHeadImpl(config, clumpName);
head.open();
if (null == writeBuffer) {
writeBuffer = ByteBuffer.allocate(config.getWriteBufferSize());
} else {
writeBuffer.clear();
}
serializer = LogSerializerFactory.getInstance().getSerializer(head.getClumpHeadEntry().getLogFileVersion());
isWriteDirect = config.isWriteDirect();
logData = new LogFile(LogFileUtil.generateDataFileName(config.getPath(), clumpName));
try {
logData.open(AccessMode.RW);
} catch (IOException e) {
throw new LogStorageException(e);
}
// append mode
logData.seek(logData.length());
headEntry = head.getClumpHeadEntry();
isOpen = true;
}
public synchronized void append(LogEntry logEntry) {
checkIsOpen();
if (fillDataToWriteBuffer(logEntry)) {
if (isWriteDirect) {
flush();
}
}
}
public void append(LogEntry[] logEntryArray) {
checkIsOpen();
for (LogEntry logEntry : logEntryArray) {
if (!fillDataToWriteBuffer(logEntry)) {
throw new LogStorageException("Writing data failed!");
}
}
if (isWriteDirect) {
flush();
}
}
private boolean fillDataToWriteBuffer(LogEntry logEntry) {
boolean needBreak = false;
do {
if (writeBuffer.hasRemaining()) {
if (serializer.writeLogEntry(writeBuffer, logEntry)) {
headEntry.addVnode(logEntry.getVnode());
return true;
} else {
flush();
needBreak = true;
continue;
}
} else {
flush();
}
if (needBreak) {
return false;
}
} while (true);
}
public synchronized boolean isOpen() {
return isOpen;
}
public synchronized boolean close() {
if (logger.isDebugEnabled()) {
logger.debug("Close writeWindow: " + this.clumpName);
// StackTraceElement[] elements = Thread.currentThread().getStackTrace();
// for (StackTraceElement e : elements) {
// logger.debug(e.toString());
// }
}
if (isOpen) {
flushAll();
head.close();
logData.close();
isOpen = false;
return true;
}
return true;
}
/**
* 将缓冲区的所有数据写入文件。
*/
public synchronized void flushAll() {
checkIsOpen();
if (null == writeBuffer) {
return;
}
try {
if (writeBuffer.hasRemaining()) {
writeBuffer.flip();
while (writeBuffer.hasRemaining()) {
logData.write(writeBuffer);
}
}
} catch (IOException e) {
throw new LogStorageException(e);
}
}
public ClumpHeadEntry getClumpHeadEntry() {
// TODO:The better way here: to return an immutable Entry Object. :)
return headEntry;
}
public synchronized boolean deleteByVnodes(List<Integer> vnodeList) {
checkIsOpen();
if (headEntry.removeVnodes(vnodeList)) {
// 刷新head信息,保存到磁盘
head.flush();
return true;
}
return false;
}
private void flush() {
// flush
try {
head.flush();
if (null != writeBuffer) {
writeBuffer.flip();
logData.write(writeBuffer);
writeBuffer.compact();
}
} catch (IOException e) {
throw new LogStorageException(e);
}
}
private void checkIsOpen() {
if (!isOpen) {
throw new WriteWindowClosedException("The write window is closed. clump:[" + this.clumpName + "]");
}
}
private LogClumpHead head;
private ClumpConfigure config;
private LogFile logData;
private String clumpName;
private ClumpHeadEntry headEntry;
private ByteBuffer writeBuffer;
private LogSerializer serializer;
private boolean isWriteDirect = false;
private volatile boolean isOpen = false;
private static final Logger logger = LoggerFactory.getLogger(DefaultWriteWindowImpl.class);
}