package com.alibaba.doris.dataserver.store.log;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import com.alibaba.doris.common.data.Key;
import com.alibaba.doris.common.data.Pair;
import com.alibaba.doris.common.data.Value;
import com.alibaba.doris.common.data.impl.ValueImpl;
import com.alibaba.doris.dataserver.store.ClosableIterator;
import com.alibaba.doris.dataserver.store.Storage;
import com.alibaba.doris.dataserver.store.StorageType;
import com.alibaba.doris.dataserver.store.log.db.BatchWriteThread;
import com.alibaba.doris.dataserver.store.log.db.ClumpConfigure;
import com.alibaba.doris.dataserver.store.log.db.LogClump;
import com.alibaba.doris.dataserver.store.log.db.LogClumpManager;
import com.alibaba.doris.dataserver.store.log.db.LogClumpsIterator;
import com.alibaba.doris.dataserver.store.log.db.LogCommand;
import com.alibaba.doris.dataserver.store.log.db.impl.AppendLogCommand;
import com.alibaba.doris.dataserver.store.log.db.impl.DeleteLogByVnodesCommand;
import com.alibaba.doris.dataserver.store.log.db.impl.ExitLogCommand;
import com.alibaba.doris.dataserver.store.log.entry.DeleteLogEntry;
import com.alibaba.doris.dataserver.store.log.entry.LogEntry;
import com.alibaba.doris.dataserver.store.log.entry.SetLogEntry;
/**
* @author ajun Email:jack.yuj@alibaba-inc.com
*/
public class LogStorage implements Storage {
public LogStorage(ClumpConfigure config) {
this.config = config;
}
public void close() {
clumpManager.releaseAllResources();
isClosed = true;
addCommand(new ExitLogCommand());
}
public void open() {
checkDatabasePath();
clumpManager = new LogClumpManager(config);
logCommandQueue = new ArrayBlockingQueue<LogCommand>(1000);
batchWriteThread = new BatchWriteThread(logCommandQueue, clumpManager);
isClosed = false;
batchWriteThread.start();
}
public boolean delete(Key key) {
if (!isClosed) {
LogEntry logEntry = new DeleteLogEntry(key, new ValueImpl(null, System.currentTimeMillis()));
return addCommand(new AppendLogCommand(logEntry));
}
return false;
}
public boolean delete(Key key, Value value) {
throw new LogStorageNotSupportedOperationException("Compare and delete command is not supported.");
}
public void set(Key key, Value value) {
if (!isClosed) {
LogEntry logEntry = new SetLogEntry(key, value);
if (!addCommand(new AppendLogCommand(logEntry))) {
throw new LogStorageException("Failed to set key:" + key);
}
}
}
public void set(Key key, Value value, boolean isSetWithCompareVersion) {
if (isSetWithCompareVersion) {
throw new LogStorageNotSupportedOperationException("Set with compare command is not supported.");
} else {
set(key, value);
}
}
/**
* return value: true:成功删除了数据;false:没有删除数据,有可能是发生异常,也可能是所给的vnodeList中的数据在log中不存在;
*/
public boolean delete(List<Integer> vnodeList) {
if (!isClosed) {
return addCommand(new DeleteLogByVnodesCommand(vnodeList));
}
return false;
}
public synchronized Value get(Key key) {
List<Integer> vnodeList = new ArrayList<Integer>(1);
vnodeList.add(key.getVNode());
final Key interKey = key;
LogClump[] clumpArray = clumpManager.listLogClumpByVnodes(vnodeList);
ClosableIterator<Pair> iterator = new LogClumpsIterator(clumpArray) {
@Override
protected boolean doFilter(LogEntry logEntry) {
if (null == logEntry) {
return false;
}
Key k = logEntry.getKey();
if (interKey.equals(k)) {
return true;
}
return false;
}
};
try {
if (iterator.hasNext()) {
Pair p = iterator.next();
return p.getValue();
}
} finally {
iterator.close();
}
return null;
}
public Map<Key, Value> getAll(Iterable<Key> keyIterator) {
throw new LogStorageNotSupportedOperationException("Gets command is not supported.");
}
public StorageType getType() {
return LogStorageType.LOG_STORAGE;
}
public Iterator<Pair> iterator() {
// TODO insert a snapshot logEntry;
return clumpManager.iterator();
}
public Iterator<Pair> iterator(List<Integer> vnodeList) {
// TODO insert a snapshot logEntry;
return clumpManager.iterator(vnodeList);
}
private void checkDatabasePath() {
String dbPath = config.getPath();
if (StringUtils.isBlank(dbPath)) {
throw new LogStorageException("Invalid database path :" + dbPath);
}
File f = new File(dbPath);
if (!f.exists()) {
try {
FileUtils.forceMkdir(f);
} catch (Exception e) {
throw new LogStorageException("Create database path failed. Path:" + dbPath, e);
}
}
}
private boolean addCommand(LogCommand command) {
try {
logCommandQueue.put(command);
command.waitingResult();
return command.isSuccess();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
private LogClumpManager clumpManager;
private ClumpConfigure config;
private BatchWriteThread batchWriteThread;
private BlockingQueue<LogCommand> logCommandQueue;
private volatile boolean isClosed = false;
// private static final Logger logger = LoggerFactory.getLogger(LogStorage.class);
}