use of io.atomix.protocols.raft.storage.snapshot.Snapshot in project atomix by atomix.
the class LeaderAppender method appendEntries.
@Override
protected void appendEntries(RaftMemberContext member) {
// Prevent recursive, asynchronous appends from being executed if the appender has been closed.
if (!open) {
return;
}
// to prevent having to read from disk to configure, install, or append to an unavailable member.
if (member.getFailureCount() >= MIN_BACKOFF_FAILURE_COUNT) {
// use exponential backoff to back off up to 60 second heartbeat intervals.
if (System.currentTimeMillis() - member.getFailureTime() > Math.min(heartbeatInterval * Math.pow(2, member.getFailureCount()), MAX_HEARTBEAT_WAIT)) {
sendAppendRequest(member, buildAppendEmptyRequest(member));
}
} else // Once the configuration is complete sendAppendRequest will be called recursively.
if (member.getConfigTerm() < raft.getTerm() || member.getConfigIndex() < raft.getCluster().getConfiguration().index()) {
if (member.canConfigure()) {
sendConfigureRequest(member, buildConfigureRequest(member));
} else if (member.canHeartbeat()) {
sendAppendRequest(member, buildAppendEmptyRequest(member));
}
} else // If there's a snapshot at the member's nextIndex, replicate the snapshot.
if (member.getMember().getType() == RaftMember.Type.ACTIVE || member.getMember().getType() == RaftMember.Type.PROMOTABLE || member.getMember().getType() == RaftMember.Type.PASSIVE) {
Snapshot snapshot = raft.getSnapshotStore().getCurrentSnapshot();
if (snapshot != null && member.getSnapshotIndex() < snapshot.index() && snapshot.index() >= member.getLogReader().getCurrentIndex()) {
if (!member.canInstall()) {
return;
}
log.debug("Replicating snapshot {} to {}", snapshot.index(), member.getMember().nodeId());
sendInstallRequest(member, buildInstallRequest(member, snapshot));
} else if (member.canAppend()) {
sendAppendRequest(member, buildAppendRequest(member, -1));
}
} else // If no AppendRequest is already being sent, send an AppendRequest.
if (member.canAppend()) {
sendAppendRequest(member, buildAppendRequest(member, -1));
}
}
use of io.atomix.protocols.raft.storage.snapshot.Snapshot in project atomix by atomix.
the class PassiveRole method onInstall.
@Override
public CompletableFuture<InstallResponse> onInstall(InstallRequest request) {
raft.checkThread();
logRequest(request);
updateTermAndLeader(request.term(), request.leader());
// If the request is for a lesser term, reject the request.
if (request.term() < raft.getTerm()) {
return CompletableFuture.completedFuture(logResponse(InstallResponse.builder().withStatus(RaftResponse.Status.ERROR).withError(RaftError.Type.ILLEGAL_MEMBER_STATE, "Request term is less than the local term " + request.term()).build()));
}
// If the snapshot already exists locally, do not overwrite it with a replicated snapshot. Simply reply to the
// request successfully.
Snapshot existingSnapshot = raft.getSnapshotStore().getSnapshot(request.snapshotIndex());
if (existingSnapshot != null) {
return CompletableFuture.completedFuture(logResponse(InstallResponse.builder().withStatus(RaftResponse.Status.OK).build()));
}
// leader dictates when a snapshot needs to be sent.
if (pendingSnapshot != null && request.snapshotIndex() != pendingSnapshot.snapshot().index()) {
pendingSnapshot.rollback();
pendingSnapshot = null;
}
// If there is no pending snapshot, create a new snapshot.
if (pendingSnapshot == null) {
// For new snapshots, the initial snapshot offset must be 0.
if (request.chunkOffset() > 0) {
return CompletableFuture.completedFuture(logResponse(InstallResponse.builder().withStatus(RaftResponse.Status.ERROR).withError(RaftError.Type.ILLEGAL_MEMBER_STATE, "Request chunk offset is invalid").build()));
}
Snapshot snapshot = raft.getSnapshotStore().newSnapshot(request.snapshotIndex(), WallClockTimestamp.from(request.snapshotTimestamp()));
pendingSnapshot = new PendingSnapshot(snapshot);
}
// If the request offset is greater than the next expected snapshot offset, fail the request.
if (request.chunkOffset() > pendingSnapshot.nextOffset()) {
return CompletableFuture.completedFuture(logResponse(InstallResponse.builder().withStatus(RaftResponse.Status.ERROR).withError(RaftError.Type.ILLEGAL_MEMBER_STATE, "Request chunk offset does not match the next chunk offset").build()));
} else // If the request offset has already been written, return OK to skip to the next chunk.
if (request.chunkOffset() < pendingSnapshot.nextOffset()) {
return CompletableFuture.completedFuture(logResponse(InstallResponse.builder().withStatus(RaftResponse.Status.OK).build()));
}
// Write the data to the snapshot.
try (SnapshotWriter writer = pendingSnapshot.snapshot().openWriter()) {
writer.write(request.data());
}
// If the snapshot is complete, store the snapshot and reset state, otherwise update the next snapshot offset.
if (request.complete()) {
pendingSnapshot.commit();
pendingSnapshot = null;
} else {
pendingSnapshot.incrementOffset();
}
return CompletableFuture.completedFuture(logResponse(InstallResponse.builder().withStatus(RaftResponse.Status.OK).build()));
}
use of io.atomix.protocols.raft.storage.snapshot.Snapshot in project atomix by atomix.
the class RaftServiceManager method install.
/**
* Prepares sessions for the given index.
*
* @param index the index for which to install snapshots
*/
private void install(long index) {
Snapshot snapshot = raft.getSnapshotStore().getSnapshot(index - 1);
// If snapshots exist for the prior index, iterate through snapshots and populate services/sessions.
if (snapshot != null) {
logger.debug("Installing snapshot {}", snapshot);
try (SnapshotReader reader = snapshot.openReader()) {
while (reader.hasRemaining()) {
int length = reader.readInt();
if (length > 0) {
SnapshotReader serviceReader = new SnapshotReader(reader.buffer().slice(length), reader.snapshot());
installService(serviceReader);
reader.skip(length);
}
}
}
}
}
use of io.atomix.protocols.raft.storage.snapshot.Snapshot in project atomix by atomix.
the class RaftServiceManager method snapshot.
/**
* Takes snapshots for the given index.
*
* @param index the index for which to take snapshots
*/
private Snapshot snapshot(long index) {
Snapshot snapshot = raft.getSnapshotStore().newTemporarySnapshot(index, new WallClockTimestamp());
try (SnapshotWriter writer = snapshot.openWriter()) {
for (RaftServiceContext service : raft.getServices()) {
writer.buffer().mark();
SnapshotWriter serviceWriter = new SnapshotWriter(writer.buffer().writeInt(0).slice(), writer.snapshot());
snapshotService(serviceWriter, service);
int length = serviceWriter.buffer().position();
writer.buffer().reset().writeInt(length).skip(length);
}
} catch (Exception e) {
snapshot.close();
throw e;
}
return snapshot;
}
Aggregations