private void becomeLeader(){ this.stopElectionTimer(); this.role = ServerRole.Leader; this.leader = this.id; this.serverToJoin = null; for(PeerServer server : this.peers.values()){ server.setNextLogIndex(this.logStore.getFirstAvailableIndex()); server.setSnapshotInSync(null); server.setFree(); this.enableHeartbeatForPeer(server); } // if current config is not committed, try to commit it if(this.config.getLogIndex() == 0){ this.config.setLogIndex(this.logStore.getFirstAvailableIndex()); this.logStore.append(new LogEntry(this.state.getTerm(), this.config.toBytes(), LogValueType.Configuration)); this.logger.info("add initial configuration to log store"); this.configChanging = true; } this.requestAppendEntries(); }
private RaftResponseMessage handleLogSyncRequest(RaftRequestMessage request){ LogEntry[] logEntries = request.getLogEntries(); RaftResponseMessage response = new RaftResponseMessage(); response.setSource(this.id); response.setDestination(request.getSource()); response.setTerm(this.state.getTerm()); response.setMessageType(RaftMessageType.SyncLogResponse); response.setNextIndex(this.logStore.getFirstAvailableIndex()); response.setAccepted(false); if(logEntries == null || logEntries.length != 1 || logEntries[0].getValueType() != LogValueType.LogPack || logEntries[0].getValue() == null || logEntries[0].getValue().length == 0){ this.logger.info("receive an invalid LogSyncRequest as the log entry value doesn't meet the requirements"); return response; } if(!this.catchingUp){ this.logger.debug("This server is ready for cluster, ignore the request"); return response; } this.logStore.applyLogPack(request.getLastLogIndex() + 1, logEntries[0].getValue()); this.commit(this.logStore.getFirstAvailableIndex() -1); response.setNextIndex(this.logStore.getFirstAvailableIndex()); response.setAccepted(true); return response; }
&& ((indexCommitted - this.logStore.getStartIndex()) > this.context.getRaftParameters().getSnapshotDistance()) && this.snapshotInProgress.compareAndSet(0, 1)){ snapshotInAction = true; while(config.getLogIndex() > indexCommitted && config.getLastLogIndex() >= this.logStore.getStartIndex()){ config = ClusterConfiguration.fromBytes(this.logStore.getLogEntryAt(config.getLastLogIndex()).getValue()); if(config.getLogIndex() > indexCommitted && config.getLastLogIndex() > 0 && config.getLastLogIndex() < this.logStore.getStartIndex()){ Snapshot lastSnapshot = this.stateMachine.getLastSnapshot(); if(lastSnapshot == null){ long logTermToCompact = this.logStore.getLogEntryAt(indexToCompact).getTerm(); Snapshot snapshot = new Snapshot(indexToCompact, logTermToCompact, config); this.stateMachine.createSnapshot(snapshot).whenCompleteAsync((Boolean result, Throwable error) -> { this.logger.debug("snapshot created, compact the log store"); try{ this.logStore.compact(snapshot.getLastLogIndex()); }catch(Throwable ex){ this.logger.error("failed to compact the log store, no worries, the system still in a good shape", ex);
private long termForLastLog(long logIndex){ if(logIndex == 0){ return 0; } if(logIndex >= this.logStore.getStartIndex()){ return this.logStore.getLogEntryAt(logIndex).getTerm(); } Snapshot lastSnapshot = this.stateMachine.getLastSnapshot(); if(lastSnapshot == null || logIndex != lastSnapshot.getLastLogIndex()){ throw new IllegalArgumentException("logIndex is beyond the range that no term could be retrieved"); } return lastSnapshot.getLastLogTerm(); }
ClusterConfiguration newConfig = new ClusterConfiguration(); newConfig.setLastLogIndex(this.config.getLogIndex()); newConfig.setLogIndex(this.logStore.getFirstAvailableIndex()); newConfig.getServers().addAll(this.config.getServers()); newConfig.getServers().add(this.serverToJoin.getClusterConfig()); LogEntry configEntry = new LogEntry(this.state.getTerm(), newConfig.toBytes(), LogValueType.Configuration); this.logStore.append(configEntry); this.configChanging = true; this.requestAppendEntries(); if(startIndex > 0 && startIndex < this.logStore.getStartIndex()){ request = this.createSyncSnapshotRequest(this.serverToJoin, startIndex, this.state.getTerm(), this.quickCommitIndex); byte[] logPack = this.logStore.packLog(startIndex, sizeToSync); request = new RaftRequestMessage(); request.setCommitIndex(this.quickCommitIndex);
(request.getLastLogIndex() < this.logStore.getFirstAvailableIndex() && request.getLastLogTerm() == this.termForLastLog(request.getLastLogIndex())); if(request.getTerm() < this.state.getTerm() || !logOkay){ response.setAccepted(false); response.setNextIndex(this.logStore.getFirstAvailableIndex()); return response; long index = request.getLastLogIndex() + 1; int logIndex = 0; while(index < this.logStore.getFirstAvailableIndex() && logIndex < logEntries.length && logEntries[logIndex].getTerm() == this.logStore.getLogEntryAt(index).getTerm()){ logIndex ++; index ++; while(index < this.logStore.getFirstAvailableIndex() && logIndex < logEntries.length){ LogEntry oldEntry = this.logStore.getLogEntryAt(index); if(oldEntry.getValueType() == LogValueType.Application){ this.stateMachine.rollback(index, oldEntry.getValue()); this.logStore.writeAt(index ++, logEntries[logIndex ++]); long indexForEntry = this.logStore.append(logEntry); if(logEntry.getValueType() == LogValueType.Configuration){ this.logger.info("received a configuration change at index %d from leader", indexForEntry);
private void requestAppendEntries(){ if(this.peers.size() == 0){ this.commit(this.logStore.getFirstAvailableIndex() - 1); return; } for(PeerServer peer : this.peers.values()){ this.requestAppendEntries(peer); } }
startingIndex = this.logStore.getStartIndex(); currentNextIndex = this.logStore.getFirstAvailableIndex(); commitIndex = this.quickCommitIndex; term = this.state.getTerm(); LogEntry[] logEntries = (lastLogIndex + 1) >= endIndex ? null : this.logStore.getLogEntries(lastLogIndex + 1, endIndex); this.logger.debug( "An AppendEntries Request for %d with LastLogIndex=%d, LastLogTerm=%d, EntriesLength=%d, CommitIndex=%d and Term=%d",
for(long i = Math.max(this.state.getCommitIndex() + 1, this.logStore.getStartIndex()); i < this.logStore.getFirstAvailableIndex(); ++i){ LogEntry logEntry = this.logStore.getLogEntryAt(i); if(logEntry.getValueType() == LogValueType.Configuration){ this.logger.info("detect a configuration change that is not committed yet at index %d", i);
private synchronized RaftResponseMessage handleVoteRequest(RaftRequestMessage request){ // we allow the server to be continue after term updated to save a round message this.updateTerm(request.getTerm()); // Reset stepping down value to prevent this server goes down when leader crashes after sending a LeaveClusterRequest if(this.steppingDown > 0){ this.steppingDown = 2; } RaftResponseMessage response = new RaftResponseMessage(); response.setMessageType(RaftMessageType.RequestVoteResponse); response.setSource(this.id); response.setDestination(request.getSource()); response.setTerm(this.state.getTerm()); boolean logOkay = request.getLastLogTerm() > this.logStore.getLastLogEntry().getTerm() || (request.getLastLogTerm() == this.logStore.getLastLogEntry().getTerm() && this.logStore.getFirstAvailableIndex() - 1 <= request.getLastLogIndex()); boolean grant = request.getTerm() == this.state.getTerm() && logOkay && (this.state.getVotedFor() == request.getSource() || this.state.getVotedFor() == -1); response.setAccepted(grant); if(grant){ this.state.setVotedFor(request.getSource()); this.context.getServerStateManager().persistState(this.state); } return response; }
long currentCommitIndex = server.state.getCommitIndex(); while(server.quickCommitIndex <= currentCommitIndex || currentCommitIndex >= server.logStore.getFirstAvailableIndex() - 1){ synchronized(this.conditionalLock){ this.conditionalLock.wait(); while(currentCommitIndex < server.quickCommitIndex && currentCommitIndex < server.logStore.getFirstAvailableIndex() - 1){ currentCommitIndex += 1; LogEntry logEntry = server.logStore.getLogEntryAt(currentCommitIndex); if(logEntry.getValueType() == LogValueType.Application){ server.stateMachine.commit(currentCommitIndex, logEntry.getValue());
if(this.logStore.compact(snapshotSyncRequest.getSnapshot().getLastLogIndex())){
ClusterConfiguration newConfig = new ClusterConfiguration(); newConfig.setLastLogIndex(this.config.getLogIndex()); newConfig.setLogIndex(this.logStore.getFirstAvailableIndex()); newConfig.getServers().addAll(this.config.getServers()); newConfig.getServers().add(this.serverToJoin.getClusterConfig()); LogEntry configEntry = new LogEntry(this.state.getTerm(), newConfig.toBytes(), LogValueType.Configuration); this.logStore.append(configEntry); this.configChanging = true; this.requestAppendEntries(); if(startIndex > 0 && startIndex < this.logStore.getStartIndex()){ request = this.createSyncSnapshotRequest(this.serverToJoin, startIndex, this.state.getTerm(), this.quickCommitIndex); byte[] logPack = this.logStore.packLog(startIndex, sizeToSync); request = new RaftRequestMessage(); request.setCommitIndex(this.quickCommitIndex);
(request.getLastLogIndex() < this.logStore.getFirstAvailableIndex() && request.getLastLogTerm() == this.termForLastLog(request.getLastLogIndex())); if(request.getTerm() < this.state.getTerm() || !logOkay){ response.setAccepted(false); response.setNextIndex(this.logStore.getFirstAvailableIndex()); return response; long index = request.getLastLogIndex() + 1; int logIndex = 0; while(index < this.logStore.getFirstAvailableIndex() && logIndex < logEntries.length && logEntries[logIndex].getTerm() == this.logStore.getLogEntryAt(index).getTerm()){ logIndex ++; index ++; while(index < this.logStore.getFirstAvailableIndex() && logIndex < logEntries.length){ LogEntry oldEntry = this.logStore.getLogEntryAt(index); if(oldEntry.getValueType() == LogValueType.Application){ this.stateMachine.rollback(index, oldEntry.getValue()); this.logStore.writeAt(index, logEntry); if (logEntry.getValueType() == LogValueType.Application) { this.stateMachine.preCommit(index, logEntry.getValue()); long indexForEntry = this.logStore.append(logEntry); if(logEntry.getValueType() == LogValueType.Configuration){ this.logger.info("received a configuration change at index %d from leader", indexForEntry);
private void requestAppendEntries(){ if(this.peers.size() == 0){ this.commit(this.logStore.getFirstAvailableIndex() - 1); return; } for(PeerServer peer : this.peers.values()){ this.requestAppendEntries(peer); } }
startingIndex = this.logStore.getStartIndex(); currentNextIndex = this.logStore.getFirstAvailableIndex(); commitIndex = this.quickCommitIndex; term = this.state.getTerm(); LogEntry[] logEntries = (lastLogIndex + 1) >= endIndex ? null : this.logStore.getLogEntries(lastLogIndex + 1, endIndex); this.logger.debug( "An AppendEntries Request for %d with LastLogIndex=%d, LastLogTerm=%d, EntriesLength=%d, CommitIndex=%d and Term=%d",
for(long i = Math.max(this.state.getCommitIndex() + 1, this.logStore.getStartIndex()); i < this.logStore.getFirstAvailableIndex(); ++i){ LogEntry logEntry = this.logStore.getLogEntryAt(i); if(logEntry.getValueType() == LogValueType.Configuration){ this.logger.info("detect a configuration change that is not committed yet at index %d", i);
private long termForLastLog(long logIndex){ if(logIndex == 0){ return 0; } if(logIndex >= this.logStore.getStartIndex()){ return this.logStore.getLogEntryAt(logIndex).getTerm(); } Snapshot lastSnapshot = this.stateMachine.getLastSnapshot(); if(lastSnapshot == null || logIndex != lastSnapshot.getLastLogIndex()){ throw new IllegalArgumentException("logIndex is beyond the range that no term could be retrieved"); } return lastSnapshot.getLastLogTerm(); }
private synchronized RaftResponseMessage handleVoteRequest(RaftRequestMessage request){ // we allow the server to be continue after term updated to save a round message this.updateTerm(request.getTerm()); // Reset stepping down value to prevent this server goes down when leader crashes after sending a LeaveClusterRequest if(this.steppingDown > 0){ this.steppingDown = 2; } RaftResponseMessage response = new RaftResponseMessage(); response.setMessageType(RaftMessageType.RequestVoteResponse); response.setSource(this.id); response.setDestination(request.getSource()); response.setTerm(this.state.getTerm()); boolean logOkay = request.getLastLogTerm() > this.logStore.getLastLogEntry().getTerm() || (request.getLastLogTerm() == this.logStore.getLastLogEntry().getTerm() && this.logStore.getFirstAvailableIndex() - 1 <= request.getLastLogIndex()); boolean grant = request.getTerm() == this.state.getTerm() && logOkay && (this.state.getVotedFor() == request.getSource() || this.state.getVotedFor() == -1); response.setAccepted(grant); if(grant){ this.state.setVotedFor(request.getSource()); this.context.getServerStateManager().persistState(this.state); } return response; }
long currentCommitIndex = server.state.getCommitIndex(); while(server.quickCommitIndex <= currentCommitIndex || currentCommitIndex >= server.logStore.getFirstAvailableIndex() - 1){ synchronized(this.conditionalLock){ this.conditionalLock.wait(); while(currentCommitIndex < server.quickCommitIndex && currentCommitIndex < server.logStore.getFirstAvailableIndex() - 1){ currentCommitIndex += 1; LogEntry logEntry = server.logStore.getLogEntryAt(currentCommitIndex); if(logEntry.getValueType() == LogValueType.Application){ server.stateMachine.commit(currentCommitIndex, logEntry.getValue());