![JAR search and dependency download from the Maven repository](/logo.png)
org.jgroups.protocols.raft.FileBasedLog Maven / Gradle / Ivy
The newest version!
package org.jgroups.protocols.raft;
import org.jgroups.Address;
import org.jgroups.raft.filelog.LogEntryStorage;
import org.jgroups.raft.filelog.MetadataStorage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.function.ObjLongConsumer;
/**
* A {@link Log} implementation stored in a file.
*
* @author Pedro Ruivo
* @since 0.5.4
*/
public class FileBasedLog implements Log {
private static final String SNAPSHOT_FILE_NAME = "state_snapshot.raft";
private File logDir;
private Address votedFor;
private long commitIndex;
private long currentTerm;
private static final boolean DEFAULT_FSYNC = true;
private boolean fsync = DEFAULT_FSYNC;
private MetadataStorage metadataStorage;
private LogEntryStorage logEntryStorage;
@Override
public void init(String log_name, Map args) throws Exception {
logDir = new File(log_name);
if (!logDir.exists() && !logDir.mkdirs()) {
throw new IllegalArgumentException("Unable to create directory " + logDir.getAbsolutePath());
} else if (!logDir.isDirectory()) {
throw new IllegalArgumentException("File " + logDir.getAbsolutePath() + " is not a directory!");
}
metadataStorage = new MetadataStorage(logDir, fsync);
metadataStorage.open();
logEntryStorage = new LogEntryStorage(logDir, fsync);
logEntryStorage.open();
commitIndex = metadataStorage.getCommitIndex();
currentTerm = metadataStorage.getCurrentTerm();
votedFor = metadataStorage.getVotedFor();
logEntryStorage.reload();
}
@Override
public Log useFsync(boolean value) {
this.fsync = value;
if (metadataStorage != null) {
metadataStorage.useFsync(value);
}
if (logEntryStorage != null) {
logEntryStorage.useFsync(value);
}
return this;
}
@Override
public boolean useFsync() {
return fsync;
}
@Override
public void close() {
try {
MetadataStorage metadataStorage = this.metadataStorage;
if (metadataStorage != null) {
metadataStorage.close();
}
LogEntryStorage entryStorage = logEntryStorage;
if (entryStorage != null) {
entryStorage.close();
}
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
@Override
public void delete() {
try {
MetadataStorage storage = metadataStorage;
if (storage != null) {
storage.delete();
}
LogEntryStorage entryStorage = logEntryStorage;
if (entryStorage != null) {
entryStorage.delete();
}
Files.deleteIfExists(snapshotPath());
if (logDir != null) {
logDir.delete(); // must be empty in order to be deleted
logDir = null;
}
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
@Override
public long currentTerm() {
return currentTerm;
}
@Override
public Log currentTerm(long new_term) {
//assert new_term >= currentTerm;
try {
checkMetadataStarted().setCurrentTerm(new_term);
currentTerm = new_term;
return this;
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
@Override
public Address votedFor() {
return votedFor;
}
@Override
public Log votedFor(Address member) {
try {
checkMetadataStarted().setVotedFor(member);
votedFor = member;
return this;
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
@Override
public long commitIndex() {
return commitIndex;
}
@Override
public Log commitIndex(long new_index) {
assert new_index >= commitIndex;
try {
checkMetadataStarted().setCommitIndex(new_index);
commitIndex = new_index;
return this;
} catch (IOException e) {
throw new IllegalStateException();
}
}
@Override
public long firstAppended() {
return checkLogEntryStorageStarted().getFirstAppended();
}
@Override
public long lastAppended() {
return checkLogEntryStorageStarted().getLastAppended();
}
public void setSnapshot(ByteBuffer sn) {
Path snapshotPath = snapshotPath();
try {
if (Files.exists(snapshotPath)) {
// write to temporary file first
Path tmp = Files.createTempFile(logDir.toPath(), null, null);
writeSnapshot(sn, tmp);
// do we need atomic move?
Files.move(tmp, snapshotPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
} else {
writeSnapshot(sn, snapshotPath);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public ByteBuffer getSnapshot() {
Path snapshotPath = snapshotPath();
if (Files.exists(snapshotPath)) {
try {
return ByteBuffer.wrap(Files.readAllBytes(snapshotPath));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return null;
}
private Path snapshotPath() {
return logDir.toPath().resolve(SNAPSHOT_FILE_NAME);
}
private static void writeSnapshot(ByteBuffer snapshot, Path path) throws IOException {
try(ByteChannel ch=Files.newByteChannel(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
ch.write(snapshot);
}
}
@Override
public long append(long index, LogEntries entries) {
assert index > firstAppended();
assert index > commitIndex();
LogEntryStorage storage = checkLogEntryStorageStarted();
try {
long term = storage.write(index, entries);
if (currentTerm != term) {
currentTerm(term);
}
} catch (IOException e) {
e.printStackTrace();
}
return lastAppended();
}
@Override
public LogEntry get(long index) {
try {
return checkLogEntryStorageStarted().getLogEntry(index);
} catch (IOException e) {
return null;
}
}
@Override
public void truncate(long index_exclusive) {
assert index_exclusive >= firstAppended();
if (index_exclusive > commitIndex) {
index_exclusive=commitIndex;
}
try {
checkLogEntryStorageStarted().removeOld(index_exclusive);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void reinitializeTo(long index, LogEntry entry) {
try {
MetadataStorage metadataStorage = checkMetadataStarted();
checkLogEntryStorageStarted().reinitializeTo(index, entry);
// update commit index
metadataStorage.setCommitIndex(index);
commitIndex = index;
// update term
if (currentTerm != entry.term()) {
metadataStorage.setCurrentTerm(entry.term());
currentTerm = entry.term();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void deleteAllEntriesStartingFrom(long start_index) {
assert start_index > commitIndex; // can we delete committed entries!? See org.jgroups.tests.LogTest.testDeleteEntriesFromFirst
assert start_index >= firstAppended();
LogEntryStorage storage = checkLogEntryStorageStarted();
try {
currentTerm(storage.removeNew(start_index));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void forEach(ObjLongConsumer function, long start_index, long end_index) {
try {
checkLogEntryStorageStarted().forEach(function, start_index, end_index);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void forEach(ObjLongConsumer function) {
forEach(function, firstAppended(), lastAppended());
}
public long sizeInBytes() {
return logEntryStorage.getCachedFileSize();
}
@Override
public String toString() {
if (logEntryStorage == null)
return "FileLog: ";
return String.format("FileLog: first=%d, commit=%d, last-appended=%d, term=%d (size=%d)",
firstAppended(), commitIndex(), lastAppended(), currentTerm, size());
}
private MetadataStorage checkMetadataStarted() {
MetadataStorage storage = metadataStorage;
if (storage == null) {
throw new IllegalStateException("Log not initialized");
}
return storage;
}
private LogEntryStorage checkLogEntryStorageStarted() {
LogEntryStorage storage = logEntryStorage;
if (storage == null) {
throw new IllegalStateException("Log not initialized");
}
return storage;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy