use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.
the class RaftNodeImpl method appendEntryAfterLeaderElection.
private void appendEntryAfterLeaderElection() {
Object entry = raftIntegration.getAppendedEntryOnLeaderElection();
if (entry != null) {
RaftLog log = state.log();
log.appendEntries(new LogEntry(state.term(), log.lastLogOrSnapshotIndex() + 1, entry));
}
}
use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.
the class RaftNodeImpl method canReplicateNewEntry.
/**
* Returns true if a new entry with the operation is currently allowed to
* be replicated. This method can be invoked only when the local Raft node
* is the leader.
* <p>
* Replication is not allowed, when;
* <ul>
* <li>Node is terminating, terminated or stepped down. See {@link RaftNodeStatus}.</li>
* <li>Raft log contains max allowed uncommitted entry count.
* See {@link RaftAlgorithmConfig#getUncommittedEntryCountToRejectNewAppends()}.</li>
* <li>The operation is a {@link RaftGroupCmd} and there's an ongoing membership change in group.</li>
* <li>The operation is a membership change operation and there's no committed entry in this term yet.
* See {@link RaftIntegration#getAppendedEntryOnLeaderElection()}.</li>
* <li>There is an ongoing leadership transfer.</li>
* </ul>
*/
public boolean canReplicateNewEntry(Object operation) {
if (isTerminatedOrSteppedDown()) {
return false;
}
RaftLog log = state.log();
long lastLogIndex = log.lastLogOrSnapshotIndex();
long commitIndex = state.commitIndex();
if (lastLogIndex - commitIndex >= maxUncommittedEntryCount) {
return false;
}
if (status == TERMINATING) {
return false;
} else if (status == UPDATING_GROUP_MEMBER_LIST) {
return state.lastGroupMembers().isKnownMember(getLocalMember()) && !(operation instanceof RaftGroupCmd);
}
if (operation instanceof UpdateRaftGroupMembersCmd) {
// the leader must have committed an entry in its term to make a membership change
// https://groups.google.com/forum/#!msg/raft-dev/t4xj6dJTP6E/d2D9LrWRza8J
// last committed entry is either in the last snapshot or still in the log
LogEntry lastCommittedEntry = commitIndex == log.snapshotIndex() ? log.snapshot() : log.getLogEntry(commitIndex);
assert lastCommittedEntry != null;
return lastCommittedEntry.term() == state.term();
}
return state.leadershipTransferState() == null;
}
use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.
the class RaftNodeImpl method applyLogEntries.
/**
* Applies committed log entries between {@code lastApplied} and {@code commitIndex}, if there's any available.
* If new entries are applied, {@link RaftState}'s {@code lastApplied} field is updated.
*
* @see RaftState#lastApplied()
* @see RaftState#commitIndex()
*/
public void applyLogEntries() {
// Reject logs we've applied already
long commitIndex = state.commitIndex();
long lastApplied = state.lastApplied();
if (commitIndex == lastApplied) {
return;
}
// If commitIndex > lastApplied: increment lastApplied, apply log[lastApplied] to state machine (§5.3)
assert commitIndex > lastApplied : "commit index: " + commitIndex + " cannot be smaller than last applied: " + lastApplied;
// Apply all the preceding logs
RaftLog raftLog = state.log();
for (long idx = state.lastApplied() + 1; idx <= commitIndex; idx++) {
LogEntry entry = raftLog.getLogEntry(idx);
if (entry == null) {
String msg = "Failed to get log entry at index: " + idx;
logger.severe(msg);
throw new AssertionError(msg);
}
applyLogEntry(entry);
// Update the lastApplied index
state.lastApplied(idx);
}
assert status != TERMINATED || commitIndex == raftLog.lastLogOrSnapshotIndex() : "commit index: " + commitIndex + " must be equal to " + raftLog.lastLogOrSnapshotIndex() + " on termination.";
if (state.role() == LEADER || state.role() == FOLLOWER) {
takeSnapshotIfCommitIndexAdvanced();
}
}
use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.
the class RaftNodeImpl method tryAdvanceCommitIndex.
public boolean tryAdvanceCommitIndex() {
// If there exists an N such that N > commitIndex, a majority of matchIndex[i] ≥ N, and log[N].term == currentTerm:
// set commitIndex = N (§5.3, §5.4)
long quorumMatchIndex = findQuorumMatchIndex();
long commitIndex = state.commitIndex();
RaftLog raftLog = state.log();
for (; quorumMatchIndex > commitIndex; quorumMatchIndex--) {
// Only log entries from the leader’s current term are committed by counting replicas; once an entry
// from the current term has been committed in this way, then all prior entries are committed indirectly
// because of the Log Matching Property.
LogEntry entry = raftLog.getLogEntry(quorumMatchIndex);
if (entry.term() == state.term()) {
commitEntries(quorumMatchIndex);
return true;
} else if (logger.isFineEnabled()) {
logger.fine("Cannot commit " + entry + " since an entry from the current term: " + state.term() + " is needed.");
}
}
return false;
}
use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.
the class PersistenceTest method testUncommittedEntriesArePersisted.
@Test
public void testUncommittedEntriesArePersisted() {
group = new LocalRaftGroupBuilder(3).setRaftStateStoreFactory(RAFT_STATE_STORE_FACTORY).build();
group.start();
final RaftNodeImpl leader = group.waitUntilLeaderElected();
RaftNodeImpl[] followers = group.getNodesExcept(leader.getLocalMember());
final RaftNodeImpl responsiveFollower = followers[0];
for (int i = 1; i < followers.length; i++) {
group.dropMessagesToMember(leader.getLocalMember(), followers[i].getLocalMember(), AppendRequest.class);
}
final int count = 10;
for (int i = 0; i < count; i++) {
leader.replicate(new ApplyRaftRunnable("val" + i));
}
assertTrueEventually(() -> {
for (RaftNodeImpl node : Arrays.asList(leader, responsiveFollower)) {
RestoredRaftState restoredState = getRestoredState(node);
LogEntry[] entries = restoredState.entries();
assertEquals(count, entries.length);
for (int i = 0; i < count; i++) {
LogEntry entry = entries[i];
assertEquals(i + 1, entry.index());
assertEquals("val" + i, ((ApplyRaftRunnable) entry.operation()).getVal());
}
}
});
}
Aggregations