package com.ctriposs.tsdb;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import com.ctriposs.tsdb.common.Level;
import com.ctriposs.tsdb.iterator.MemSeekIterator;
import com.ctriposs.tsdb.iterator.SeekIteratorAdapter;
import com.ctriposs.tsdb.level.StoreLevel;
import com.ctriposs.tsdb.manage.FileManager;
import com.ctriposs.tsdb.manage.NameManager;
import com.ctriposs.tsdb.table.InternalKeyComparator;
import com.ctriposs.tsdb.table.MapFileLogReader;
import com.ctriposs.tsdb.table.MemTable;
import com.ctriposs.tsdb.util.FileUtil;
public class DBEngine implements IDB {
/** The engine config*/
private DBConfig config;
/** The active memory table*/
private MemTable memTable;
/** Store memory table to file*/
private StoreLevel storeLevel;
/** Compact files */
private Map<Integer, Level> compactLevelMap;
/** Manage the file by time sequence */
private FileManager fileManager;
/** Map name to code for table and column*/
private NameManager nameManager;
/** The comparator for key*/
private InternalKeyComparator internalKeyComparator;
/** The memory table change lock. */
private final Lock lock = new ReentrantLock();
/** The hit counter. */
private AtomicLong hitCounter = new AtomicLong();
/** The miss counter. */
private AtomicLong missCounter = new AtomicLong();
/** The get counter. */
private AtomicLong getCounter = new AtomicLong();
/** The put counter. */
private AtomicLong putCounter = new AtomicLong();
/** The delete counter. */
private AtomicLong deleteCounter = new AtomicLong();
public DBEngine(DBConfig config) throws IOException {
this.config = config;
if(config.getInternalKeyComparator() == null){
this.internalKeyComparator = new InternalKeyComparator();
}else{
this.internalKeyComparator = config.getInternalKeyComparator();
}
FileUtil.cleanDirectory(new File(config.getDBDir()));
this.nameManager = new NameManager(config.getDBDir());
this.fileManager = new FileManager(config.getDBDir(),config.getMaxPeriod(), internalKeyComparator, nameManager);
this.memTable = new MemTable(config.getDBDir(), fileManager.getFileNumber(), config.getFileCapacity(), config.getMaxMemTableSize(), internalKeyComparator);
this.storeLevel = new StoreLevel(fileManager, config.getStoreThread(), config.getMaxMemTable(), MemTable.MINUTE);
this.compactLevelMap = new LinkedHashMap<Integer, Level>();
this.storeLevel.start();
//this.compactLevelMap.put(1, new CompactLevel(fileManager, this.storeLevel, 1, 4 * 60 * 1000, 1));
//initialize compact level
for(Entry<Integer,Level> entry : compactLevelMap.entrySet()){
//entry.getValue().recoveryData();
entry.getValue().start();
}
//this.fileManager.recoveryName();
//this.storeLevel.recoveryData();
//recoveryLog();
}
private void recoveryLog()throws IOException{
List<File> files = FileUtil.listFiles(new File(fileManager.getStoreDir()), "log");
for(File file:files){
if(file.getPath().equals(memTable.getLogFile())){
continue;
}
String name[] = file.getName().split("[-|.]");
long fileNumber = Long.parseLong(name[1]);
fileManager.upateFileNumber(fileNumber);
ILogReader logReader = new MapFileLogReader(file,fileNumber,internalKeyComparator);
try {
MemTable memTable = logReader.getMemTable();
memTable.close();
logReader.close();
if(!memTable.isEmpty()){
storeLevel.addMemTable(memTable);
}else{
FileUtil.forceDelete(file);
}
} catch (Exception e) {
throw new IOException(e);
}
}
}
@Override
public void put(String tableName, String colName, long time, byte[] value) throws IOException{
putCounter.incrementAndGet();
InternalKey key = new InternalKey(nameManager.getCode(tableName), nameManager.getCode(colName), time);
if(!memTable.add(key, value)) {
try {
lock.lock();
if(!memTable.add(key, value)){
try {
memTable.close();
storeLevel.addMemTable(memTable);
memTable = new MemTable(config.getDBDir(), fileManager.getFileNumber(), config.getFileCapacity(), config.getMaxMemTableSize(), internalKeyComparator);
memTable.add(key, value);
} catch (Exception e) {
throw new IOException(e);
}
}
} finally {
lock.unlock();
}
}
}
@Override
public byte[] get(String tableName, String colName, long time) throws IOException {
getCounter.incrementAndGet();
InternalKey key = new InternalKey(nameManager.getCode(tableName), nameManager.getCode(colName), time);
byte[] value = memTable.getValue(key);
if(value == null) {
value = storeLevel.getValue(key);
if(value == null){
for(Entry<Integer,Level> entry:compactLevelMap.entrySet()){
value = entry.getValue().getValue(key);
if(value != null){
return value;
}
}
}
}
return value;
}
@Override
public void delete(long afterTime) throws IOException {
deleteCounter.incrementAndGet();
this.storeLevel.delete(afterTime);
for(Entry<Integer,Level> entry:compactLevelMap.entrySet()){
entry.getValue().delete(afterTime);
}
}
@Override
public ISeekIterator<InternalKey, byte[]> iterator() {
SeekIteratorAdapter it = new SeekIteratorAdapter(fileManager, storeLevel.iterator());
for(Entry<Integer,Level> entry:compactLevelMap.entrySet()){
it.addIterator(entry.getValue().iterator());
}
for(MemSeekIterator memit:storeLevel.getAllMemSeekIterator()){
it.addIterator(memit);
}
it.addIterator(memTable.iterator(fileManager));
return it;
}
@Override
public void close() throws IOException {
nameManager.close();
storeLevel.stop();
for(Entry<Integer,Level> entry:compactLevelMap.entrySet()){
entry.getValue().stop();
}
}
public long getHitCounter() {
return hitCounter.get();
}
public long getMissCounter() {
return missCounter.get();
}
public long getGetCounter() {
return getCounter.get();
}
public long getPutCounter() {
return putCounter.get();
}
public long getDeleteCounter() {
return deleteCounter.get();
}
public long getStoreCounter(int level){
if(level == 0){
return storeLevel.getStoreCounter();
}else{
Level sLevel = compactLevelMap.get(level);
if(sLevel != null){
return sLevel.getStoreCounter();
}else{
return 0;
}
}
}
public long getStoreErrorCounter(int level){
if(level == 0){
return storeLevel.getStoreErrorCounter();
}else{
Level sLevel = compactLevelMap.get(level);
if(sLevel != null){
return sLevel.getStoreErrorCounter();
}else{
return 0;
}
}
}
}