![JAR search and dependency download from the Maven repository](/logo.png)
com.github.ltsopensource.kv.txlog.StoreTxLogEngine Maven / Gradle / Ivy
package com.github.ltsopensource.kv.txlog;
import com.github.ltsopensource.core.commons.file.FileUtils;
import com.github.ltsopensource.core.commons.io.UnsafeByteArrayInputStream;
import com.github.ltsopensource.core.commons.io.UnsafeByteArrayOutputStream;
import com.github.ltsopensource.core.json.TypeReference;
import com.github.ltsopensource.kv.*;
import com.github.ltsopensource.kv.serializer.StoreSerializer;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Robert HG ([email protected]) on 12/13/15.
*/
public class StoreTxLogEngine {
private volatile StoreTxLog storeTxLog;
private StoreSerializer serializer;
private AtomicBoolean initialed = new AtomicBoolean(false);
private List storeTxLogs;
private StoreConfig storeConfig;
private static final String LOG_FILE_SUFFIX = StoreTxLog.LOG_FILE_SUFFIX;
// Log 目录
private File logPath;
public StoreTxLogEngine(StoreSerializer serializer, StoreConfig storeConfig) {
this.logPath = storeConfig.getLogPath();
this.storeConfig = storeConfig;
this.serializer = serializer;
this.storeTxLogs = new CopyOnWriteArrayList();
}
public void init() throws IOException {
if (!initialed.compareAndSet(false, true)) {
return;
}
// 从path里面读取老的Log文件
FileUtils.createDirIfNotExist(logPath);
String[] logFiles = logPath.list(new FilenameFilter() {
@Override
public boolean accept(File file, String name) {
return name.endsWith(LOG_FILE_SUFFIX);
}
});
if (logFiles == null) {
throw new IOException("can't list file in " + logPath);
}
if (logFiles.length > 0) {
Arrays.sort(logFiles, new Comparator() {
@Override
public int compare(String left, String right) {
return left.compareTo(right);
}
});
for (int i = 0; i < logFiles.length; i++) {
String logFile = logFiles[i];
boolean isWritable = ++i == logFiles.length;
// 只有最后一个文件才是可写的
StoreTxLog storeTxLog = new StoreTxLog(
storeConfig, new File(logPath, logFile), !isWritable, false, 0);
if (i > 1) {
storeTxLogs.get(i - 1).setNext(storeTxLog);
}
storeTxLogs.add(storeTxLog);
if (isWritable) {
this.storeTxLog = storeTxLog;
}
}
} else {
// 新建一个文件
String name = System.currentTimeMillis() + LOG_FILE_SUFFIX;
this.storeTxLog = new StoreTxLog(storeConfig, new File(logPath, name), false, true, 0);
storeTxLogs.add(storeTxLog);
}
}
private StoreTxLog nextNewStoreTxLog() throws IOException {
long firstRecordId = storeTxLog.getNextRecordId();
String name = System.currentTimeMillis() + LOG_FILE_SUFFIX;
StoreTxLog newStoreTxLog = new StoreTxLog(storeConfig, new File(logPath, name), false, true, firstRecordId);
storeTxLogs.add(newStoreTxLog);
storeTxLog.setNext(newStoreTxLog);
storeTxLog = newStoreTxLog;
return storeTxLog;
}
/**
* 追加一条事务日志
*/
public StoreTxLogPosition append(Operation op, K key) throws DBException {
return this.append(op, key, null);
}
/**
* 追加一条事务日志
*/
public StoreTxLogPosition append(Operation op, K key, V value) throws DBException {
try {
try {
return this.append(storeTxLog, op, key, value);
} catch (CapacityNotEnoughException notEnough) {
// 要新建一个文件
return this.append(nextNewStoreTxLog(), op, key, value);
}
} catch (Exception e) {
throw new DBException("append dbLog error:" + e.getMessage(), e);
}
}
private StoreTxLogPosition append(StoreTxLog storeTxLog, Operation op, K key, V value) throws IOException {
StoreTxLogEntry entry = null;
long timestamp = System.currentTimeMillis();
switch (op) {
case PUT:
entry = new StoreTxLogEntry(op, key, value, timestamp);
break;
case REMOVE:
entry = new StoreTxLogEntry(op, key, timestamp);
break;
}
UnsafeByteArrayOutputStream out = new UnsafeByteArrayOutputStream();
try {
serializer.serialize(entry, out);
byte[] entryBytes = out.toByteArray();
return storeTxLog.append(entryBytes);
} finally {
out.close();
}
}
public Cursor> cursor(StoreTxLogPosition position) {
long recordId = position.getRecordId();
if (storeTxLogs.size() == 0) {
return new EmptyCursor>();
}
StoreTxLog targetTxLog = null;
for (StoreTxLog txLog : storeTxLogs) {
if (recordId >= txLog.getFirstRecordId() && recordId < txLog.getNextRecordId()) {
targetTxLog = txLog;
break;
}
}
if (targetTxLog == null) {
return new EmptyCursor>();
}
return new StoreTxLogCursor(targetTxLog, recordId - targetTxLog.getFirstRecordId());
}
private class StoreTxLogCursor implements Cursor> {
private StoreTxLog currentTxLog;
private long position;
public StoreTxLogCursor(StoreTxLog currentTxLog, long position) {
this.currentTxLog = currentTxLog;
if (position <= 0) {
position = currentTxLog.getHeaderLength();
}
this.position = position;
}
@Override
public boolean hasNext() {
// 1. 判断该文件是否还有内容
if (currentTxLog == null) {
return false;
}
if (position < currentTxLog.getFileLength()) {
return true;
}
// 2. 如果该文件没有内容了,判断是否还有下一个文件
if (currentTxLog.next() == null) {
return false;
}
currentTxLog = currentTxLog.next();
position = currentTxLog.getHeaderLength();
return true;
}
@Override
public StoreTxLogCursorEntry next() {
try {
byte[] entry = currentTxLog.readEntry(position);
int entryLength = entry.length;
UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream(entry);
StoreTxLogEntry storeTxLogEntry = serializer.deserialize(is, new TypeReference>() {
}.getType());
StoreTxLogCursorEntry storeTxLogCursorEntry = new StoreTxLogCursorEntry();
storeTxLogCursorEntry.setStoreTxLogEntry(storeTxLogEntry);
storeTxLogCursorEntry.setPosition(new StoreTxLogPosition(currentTxLog.getFirstRecordId() + position));
this.position = currentTxLog.nextEntryPosition(position, entryLength);
return storeTxLogCursorEntry;
} catch (IOException e) {
throw new DBException("Cursor next() error:" + e.getMessage(), e);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy