net.kuujo.copycat.state.internal.SnapshottableLogManager Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.kuujo.copycat.state.internal;
import net.kuujo.copycat.log.LogConfig;
import net.kuujo.copycat.log.LogManager;
import net.kuujo.copycat.log.LogSegment;
import net.kuujo.copycat.util.internal.Assert;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.TreeMap;
/**
* Snapshottable log manager.
*
* @author Jordan Halterman
*/
public class SnapshottableLogManager implements LogManager {
private final LogManager logManager;
private final LogManager snapshotManager;
public SnapshottableLogManager(LogManager logManager, LogManager snapshotManager) {
this.logManager = logManager;
this.snapshotManager = snapshotManager;
}
@Override
public LogConfig config() {
return logManager.config();
}
@Override
public TreeMap segments() {
return logManager.segments();
}
@Override
public LogSegment segment() {
return logManager.segment();
}
@Override
public LogSegment segment(long index) {
return snapshotManager.lastIndex() == null || index > snapshotManager.lastIndex() ? logManager.segment(index) : snapshotManager.segment(index);
}
@Override
public LogSegment firstSegment() {
return snapshotManager.isEmpty() ? logManager.firstSegment() : snapshotManager.firstSegment();
}
@Override
public LogSegment lastSegment() {
return logManager.lastSegment();
}
@Override
public void open() throws IOException {
snapshotManager.open();
logManager.open();
}
@Override
public boolean isEmpty() {
return snapshotManager.isEmpty() && logManager.isEmpty();
}
@Override
public boolean isOpen() {
return snapshotManager.isOpen() && logManager.isOpen();
}
@Override
public long size() {
return snapshotManager.size() + logManager.size();
}
@Override
public long entryCount() {
return snapshotManager.entryCount() + logManager.entryCount();
}
/**
* Returns a boolean value indicating whether the given index is a snapshottable index.
*
* @param index The index to check.
* @return Indicates whether a snapshot can be taken at the given index.
*/
public boolean isSnapshottable(long index) {
if (!logManager.containsIndex(index)) {
return false;
}
LogSegment segment = logManager.segment(index);
if (segment == null) {
return false;
} else if (segment.lastIndex() == null || segment.lastIndex() != index) {
return false;
} else if (segment == logManager.lastSegment()) {
return false;
}
return true;
}
/**
* Appends a snapshot to the log.
*
* @param index The index at which to write the snapshot.
* @param snapshot The snapshot to append to the snapshot log.
* @return The index at which the snapshot was written.
* @throws IOException If the log could not be rolled over.
*/
public long appendSnapshot(long index, List snapshot) throws IOException {
LogSegment segment = logManager.segment(index);
if (segment == null) {
throw new IndexOutOfBoundsException("Invalid snapshot index " + index);
} else if (segment.lastIndex() != index) {
throw new IllegalArgumentException("Snapshot index must be the last index of a segment");
} else if (segment == logManager.lastSegment()) {
throw new IllegalArgumentException("Cannot snapshot current log segment");
}
// When appending a snapshot, force the snapshot log manager to roll over to a new segment, append the snapshot
// to the log, and then compact the log once the snapshot has been appended.
snapshotManager.rollOver(index - snapshot.size() + 1);
for (ByteBuffer entry : snapshot) {
snapshotManager.appendEntry(entry);
}
// Compact the snapshot and user logs in order to ensure old entries do not remain in the logs.
snapshotManager.compact(index - snapshot.size() + 1);
logManager.compact(index + 1);
return index;
}
@Override
public long appendEntry(ByteBuffer entry) throws IOException {
return logManager.appendEntry(entry);
}
@Override
public long index() {
return snapshotManager.index();
}
@Override
public Long firstIndex() {
return !snapshotManager.isEmpty() ? snapshotManager.firstIndex() : logManager.firstIndex();
}
@Override
public Long lastIndex() {
return logManager.lastIndex();
}
@Override
public boolean containsIndex(long index) {
Assert.state(isOpen(), "Log is not open");
return logManager.containsIndex(index) || snapshotManager.containsIndex(index);
}
@Override
public ByteBuffer getEntry(long index) {
Assert.state(isOpen(), "Log is not open");
if (logManager.containsIndex(index)) {
return logManager.getEntry(index);
} else if (snapshotManager.containsIndex(index)) {
return snapshotManager.getEntry(index);
}
throw new IndexOutOfBoundsException("No entry at index " + index);
}
@Override
public void removeAfter(long index) {
Assert.state(isOpen(), "Log is not open");
Assert.index(index, logManager.containsIndex(index), "Log index out of bounds");
logManager.removeAfter(index);
}
@Override
public void split(long index) throws IOException {
logManager.split(index);
}
@Override
public void rollOver(long index) throws IOException {
logManager.rollOver(index);
snapshotManager.rollOver(index);
}
@Override
public void compact(long index) throws IOException {
logManager.compact(index);
snapshotManager.compact(index);
}
@Override
public void flush() {
logManager.flush();
}
@Override
public void close() throws IOException {
logManager.close();
snapshotManager.close();
}
@Override
public boolean isClosed() {
return logManager.isClosed();
}
@Override
public void delete() {
logManager.delete();
snapshotManager.delete();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy