Search in sources :

Example 1 with RaftLogWriter

use of io.atomix.protocols.raft.storage.log.RaftLogWriter in project atomix by atomix.

the class PassiveRole method checkPreviousEntry.

/**
 * Checks the previous index of the given AppendRequest, returning a boolean indicating whether to continue
 * handling the request.
 */
protected boolean checkPreviousEntry(AppendRequest request, CompletableFuture<AppendResponse> future) {
    RaftLogWriter writer = raft.getLogWriter();
    RaftLogReader reader = raft.getLogReader();
    // with a zero term in the event the leader has compacted its logs and is sending the first entry.
    if (request.prevLogTerm() != 0) {
        // Get the last entry written to the log.
        Indexed<RaftLogEntry> lastEntry = writer.getLastEntry();
        // If the local log is non-empty...
        if (lastEntry != null) {
            // If the previous log index is greater than the last entry index, fail the attempt.
            if (request.prevLogIndex() > lastEntry.index()) {
                log.debug("Rejected {}: Previous index ({}) is greater than the local log's last index ({})", request, request.prevLogIndex(), lastEntry.index());
                return failAppend(lastEntry.index(), future);
            }
            // If the previous log index is less than the last written entry index, look up the entry.
            if (request.prevLogIndex() < lastEntry.index()) {
                // Reset the reader to the previous log index.
                if (reader.getNextIndex() != request.prevLogIndex()) {
                    reader.reset(request.prevLogIndex());
                }
                // The previous entry should exist in the log if we've gotten this far.
                if (!reader.hasNext()) {
                    log.debug("Rejected {}: Previous entry does not exist in the local log", request);
                    return failAppend(lastEntry.index(), future);
                }
                // Read the previous entry and validate that the term matches the request previous log term.
                Indexed<RaftLogEntry> previousEntry = reader.next();
                if (request.prevLogTerm() != previousEntry.entry().term()) {
                    log.debug("Rejected {}: Previous entry term ({}) does not match local log's term for the same entry ({})", request, request.prevLogTerm(), previousEntry.entry().term());
                    return failAppend(request.prevLogIndex() - 1, future);
                }
            } else // If the previous log term doesn't equal the last entry term, fail the append, sending the prior entry.
            if (request.prevLogTerm() != lastEntry.entry().term()) {
                log.debug("Rejected {}: Previous entry term ({}) does not equal the local log's last term ({})", request, request.prevLogTerm(), lastEntry.entry().term());
                return failAppend(request.prevLogIndex() - 1, future);
            }
        } else {
            // If the previous log index is set and the last entry is null, fail the append.
            if (request.prevLogIndex() > 0) {
                log.debug("Rejected {}: Previous index ({}) is greater than the local log's last index (0)", request, request.prevLogIndex());
                return failAppend(0, future);
            }
        }
    }
    return true;
}
Also used : RaftLogReader(io.atomix.protocols.raft.storage.log.RaftLogReader) RaftLogWriter(io.atomix.protocols.raft.storage.log.RaftLogWriter) RaftLogEntry(io.atomix.protocols.raft.storage.log.entry.RaftLogEntry)

Example 2 with RaftLogWriter

use of io.atomix.protocols.raft.storage.log.RaftLogWriter in project atomix by atomix.

the class PassiveRole method appendEntries.

/**
 * Appends entries from the given AppendRequest.
 */
protected void appendEntries(AppendRequest request, CompletableFuture<AppendResponse> future) {
    // Compute the last entry index from the previous log index and request entry count.
    final long lastEntryIndex = request.prevLogIndex() + request.entries().size();
    // Ensure the commitIndex is not increased beyond the index of the last entry in the request.
    final long commitIndex = Math.max(raft.getCommitIndex(), Math.min(request.commitIndex(), lastEntryIndex));
    // Track the last log index while entries are appended.
    long lastLogIndex = request.prevLogIndex();
    if (!request.entries().isEmpty()) {
        final RaftLogWriter writer = raft.getLogWriter();
        final RaftLogReader reader = raft.getLogReader();
        // Reset the log to the previous index plus one.
        if (request.prevLogTerm() == 0) {
            log.debug("Reset first index to {}", request.prevLogIndex() + 1);
            writer.reset(request.prevLogIndex() + 1);
        }
        // Iterate through entries and append them.
        for (RaftLogEntry entry : request.entries()) {
            long index = ++lastLogIndex;
            // Get the last entry written to the log by the writer.
            Indexed<RaftLogEntry> lastEntry = writer.getLastEntry();
            if (lastEntry != null) {
                // we need to validate that the entry that's already in the log matches this entry.
                if (lastEntry.index() > index) {
                    // Reset the reader to the current entry index.
                    if (reader.getNextIndex() != index) {
                        reader.reset(index);
                    }
                    // If the reader does not have any next entry, that indicates an inconsistency between the reader and writer.
                    if (!reader.hasNext()) {
                        throw new IllegalStateException("Log reader inconsistent with log writer");
                    }
                    // Read the existing entry from the log.
                    Indexed<RaftLogEntry> existingEntry = reader.next();
                    // the log and append the leader's entry.
                    if (existingEntry.entry().term() != entry.term()) {
                        writer.truncate(index - 1);
                        if (!appendEntry(index, entry, writer, future)) {
                            return;
                        }
                    }
                } else // to read the entry from disk and can just compare the last entry in the writer.
                if (lastEntry.index() == index) {
                    // the log and append the leader's entry.
                    if (lastEntry.entry().term() != entry.term()) {
                        writer.truncate(index - 1);
                        if (!appendEntry(index, entry, writer, future)) {
                            return;
                        }
                    }
                } else // Otherwise, this entry is being appended at the end of the log.
                {
                    // If the last entry index isn't the previous index, throw an exception because something crazy happened!
                    if (lastEntry.index() != index - 1) {
                        throw new IllegalStateException("Log writer inconsistent with next append entry index " + index);
                    }
                    // Append the entry and log a message.
                    if (!appendEntry(index, entry, writer, future)) {
                        return;
                    }
                }
            } else // Otherwise, if the last entry is null just append the entry and log a message.
            {
                if (!appendEntry(index, entry, writer, future)) {
                    return;
                }
            }
            // If the last log index meets the commitIndex, break the append loop to avoid appending uncommitted entries.
            if (!role().active() && index == commitIndex) {
                break;
            }
        }
    }
    // Set the first commit index.
    raft.setFirstCommitIndex(request.commitIndex());
    // Update the context commit and global indices.
    long previousCommitIndex = raft.setCommitIndex(commitIndex);
    if (previousCommitIndex < commitIndex) {
        log.trace("Committed entries up to index {}", commitIndex);
        raft.getServiceManager().applyAll(commitIndex);
    }
    // Return a successful append response.
    succeedAppend(lastLogIndex, future);
}
Also used : RaftLogReader(io.atomix.protocols.raft.storage.log.RaftLogReader) RaftLogWriter(io.atomix.protocols.raft.storage.log.RaftLogWriter) RaftLogEntry(io.atomix.protocols.raft.storage.log.entry.RaftLogEntry)

Example 3 with RaftLogWriter

use of io.atomix.protocols.raft.storage.log.RaftLogWriter in project atomix by atomix.

the class PassiveRole method truncateUncommittedEntries.

/**
 * Truncates uncommitted entries from the log.
 */
private void truncateUncommittedEntries() {
    if (role() == RaftServer.Role.PASSIVE) {
        final RaftLogWriter writer = raft.getLogWriter();
        writer.truncate(raft.getCommitIndex());
    }
}
Also used : RaftLogWriter(io.atomix.protocols.raft.storage.log.RaftLogWriter)

Aggregations

RaftLogWriter (io.atomix.protocols.raft.storage.log.RaftLogWriter)3 RaftLogReader (io.atomix.protocols.raft.storage.log.RaftLogReader)2 RaftLogEntry (io.atomix.protocols.raft.storage.log.entry.RaftLogEntry)2