public static RaftMessageSender run(RaftContext context){ if(context == null){ throw new IllegalArgumentException("context cannot be null"); } RaftServer server = new RaftServer(context); context.getRpcListener().startListening(server); return server.createMessageSender(); } }
private boolean updateTerm(long term){ if(term > this.state.getTerm()){ this.state.setTerm(term); this.state.setVotedFor(-1); this.electionCompleted = false; this.votesGranted = 0; this.votedServers.clear(); this.context.getServerStateManager().persistState(this.state); this.becomeFollower(); return true; } return false; }
private void handleVotingResponse(RaftResponseMessage response){ this.votesResponded += 1; if(this.electionCompleted){ this.logger.info("Election completed, will ignore the voting result from this server"); return; } if(response.isAccepted()){ this.votesGranted += 1; } if(this.votesResponded >= this.peers.size() + 1){ this.electionCompleted = true; } // got a majority set of granted votes if(this.votesGranted > (this.peers.size() + 1) / 2){ this.logger.info("Server is elected as leader for term %d", this.state.getTerm()); this.electionCompleted = true; this.becomeLeader(); } }
private void requestAppendEntries(){ if(this.peers.size() == 0){ this.commit(this.logStore.getFirstAvailableIndex() - 1); return; } for(PeerServer peer : this.peers.values()){ this.requestAppendEntries(peer); } }
private synchronized RaftResponseMessage handleExtendedMessages(RaftRequestMessage request){ if(request.getMessageType() == RaftMessageType.AddServerRequest){ return this.handleAddServerRequest(request); }else if(request.getMessageType() == RaftMessageType.RemoveServerRequest){ return this.handleRemoveServerRequest(request); }else if(request.getMessageType() == RaftMessageType.SyncLogRequest){ return this.handleLogSyncRequest(request); }else if(request.getMessageType() == RaftMessageType.JoinClusterRequest){ return this.handleJoinClusterRequest(request); }else if(request.getMessageType() == RaftMessageType.LeaveClusterRequest){ return this.handleLeaveClusterRequest(request); }else if(request.getMessageType() == RaftMessageType.InstallSnapshotRequest){ return this.handleInstallSnapshotRequest(request); }else{ this.logger.error("receive an unknown request %s, for safety, step down.", request.getMessageType().toString()); System.exit(-1); } return null; }
private synchronized RaftResponseMessage handleAppendEntriesRequest(RaftRequestMessage request){ this.updateTerm(request.getTerm()); this.becomeFollower(); }else if(this.role == ServerRole.Leader){ this.logger.error("Receive AppendEntriesRequest from another leader(%d) with same term, there must be a bug, server exits", request.getSource()); System.exit(-1); }else{ this.restartElectionTimer(); request.getLastLogTerm() == this.termForLastLog(request.getLastLogIndex())); if(request.getTerm() < this.state.getTerm() || !logOkay){ response.setAccepted(false); this.commit(request.getCommitIndex()); response.setAccepted(true); response.setNextIndex(request.getLastLogIndex() + (request.getLogEntries() == null ? 0 : request.getLogEntries().length) + 1);
private RaftResponseMessage handleInstallSnapshotRequest(RaftRequestMessage request){ this.updateTerm(request.getTerm()); this.becomeFollower(); }else if(this.role == ServerRole.Leader){ this.logger.error("Receive InstallSnapshotRequest from another leader(%d) with same term, there must be a bug, server exits", request.getSource()); System.exit(-1); }else{ this.restartElectionTimer(); response.setAccepted(this.handleSnapshotSyncRequest(snapshotSyncRequest));
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 void requestVote(){ // vote for self this.logger.info("requestVote started with term %d", this.state.getTerm()); this.state.setVotedFor(this.id); this.context.getServerStateManager().persistState(this.state); this.votesGranted += 1; this.votedServers.add(this.id); // this is the only server? if(this.votesGranted > (this.peers.size() + 1) / 2){ this.electionCompleted = true; this.becomeLeader(); return; } for(PeerServer peer : this.peers.values()){ RaftRequestMessage request = new RaftRequestMessage(); request.setMessageType(RaftMessageType.RequestVoteRequest); request.setDestination(peer.getId()); request.setSource(this.id); request.setLastLogIndex(this.logStore.getFirstAvailableIndex() - 1); request.setLastLogTerm(this.termForLastLog(this.logStore.getFirstAvailableIndex() - 1)); request.setTerm(this.state.getTerm()); this.logger.debug("send %s to server %d with term %d", RaftMessageType.RequestVoteRequest.toString(), peer.getId(), this.state.getTerm()); peer.SendRequest(request).whenCompleteAsync((RaftResponseMessage response, Throwable error) -> { handlePeerResponse(response, error); }, this.context.getScheduledExecutor()); } }
this.logStore.append(configEntry); this.configChanging = true; this.requestAppendEntries(); return; request = this.createSyncSnapshotRequest(this.serverToJoin, startIndex, this.state.getTerm(), this.quickCommitIndex); this.handleExtendedResponse(response, error); }, this.context.getScheduledExecutor());
private boolean requestAppendEntries(PeerServer peer){ if(peer.makeBusy()){ peer.SendRequest(this.createAppendEntriesRequest(peer)) .whenCompleteAsync((RaftResponseMessage response, Throwable error) -> { try{ handlePeerResponse(response, error); }catch(Throwable err){ this.logger.error("Uncaught exception %s", err.toString()); } }, this.context.getScheduledExecutor()); return true; } this.logger.debug("Server %d is busy, skip the request", peer.getId()); return false; }
return this.createSyncSnapshotRequest(peer, lastLogIndex, term, commitIndex); long lastLogTerm = this.termForLastLog(lastLogIndex); long endIndex = Math.min(currentNextIndex, lastLogIndex + 1 + context.getRaftParameters().getMaximumAppendingSize()); LogEntry[] logEntries = (lastLogIndex + 1) >= endIndex ? null : this.logStore.getLogEntries(lastLogIndex + 1, endIndex);
PeerServer peer = new PeerServer(server, context, peerServer -> this.handleHeartbeatTimeout(peerServer)); peer.setNextLogIndex(this.logStore.getFirstAvailableIndex()); this.peers.put(server.getId(), peer); if(this.role == ServerRole.Leader){ this.logger.info("enable heartbeating for server %d", peer.getId()); this.enableHeartbeatForPeer(peer); if(this.serverToJoin != null && this.serverToJoin.getId() == peer.getId()){ peer.setNextLogIndex(this.serverToJoin.getNextLogIndex());
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; }
int blockSize = this.getSnapshotSyncBlockSize(); byte[] data = new byte[sizeLeft > blockSize ? blockSize : (int)sizeLeft]; try{
private synchronized RaftResponseMessage handleExtendedMessages(RaftRequestMessage request){ if(request.getMessageType() == RaftMessageType.AddServerRequest){ return this.handleAddServerRequest(request); }else if(request.getMessageType() == RaftMessageType.RemoveServerRequest){ return this.handleRemoveServerRequest(request); }else if(request.getMessageType() == RaftMessageType.SyncLogRequest){ return this.handleLogSyncRequest(request); }else if(request.getMessageType() == RaftMessageType.JoinClusterRequest){ return this.handleJoinClusterRequest(request); }else if(request.getMessageType() == RaftMessageType.LeaveClusterRequest){ return this.handleLeaveClusterRequest(request); }else if(request.getMessageType() == RaftMessageType.InstallSnapshotRequest){ return this.handleInstallSnapshotRequest(request); }else{ this.logger.error("receive an unknown request %s, for safety, step down.", request.getMessageType().toString()); this.stateMachine.exit(-1); } return null; }
private synchronized RaftResponseMessage handleAppendEntriesRequest(RaftRequestMessage request){ this.updateTerm(request.getTerm()); this.becomeFollower(); }else if(this.role == ServerRole.Leader){ this.logger.error("Receive AppendEntriesRequest from another leader(%d) with same term, there must be a bug, server exits", request.getSource()); this.stateMachine.exit(-1); }else{ this.restartElectionTimer(); request.getLastLogTerm() == this.termForLastLog(request.getLastLogIndex())); if(request.getTerm() < this.state.getTerm() || !logOkay){ response.setAccepted(false); this.commit(request.getCommitIndex()); response.setAccepted(true); response.setNextIndex(request.getLastLogIndex() + (request.getLogEntries() == null ? 0 : request.getLogEntries().length) + 1);
private RaftResponseMessage handleInstallSnapshotRequest(RaftRequestMessage request){ this.updateTerm(request.getTerm()); this.becomeFollower(); }else if(this.role == ServerRole.Leader){ this.logger.error("Receive InstallSnapshotRequest from another leader(%d) with same term, there must be a bug, server exits", request.getSource()); this.stateMachine.exit(-1); }else{ this.restartElectionTimer(); response.setAccepted(this.handleSnapshotSyncRequest(snapshotSyncRequest));
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 void requestVote(){ // vote for self this.logger.info("requestVote started with term %d", this.state.getTerm()); this.state.setVotedFor(this.id); this.context.getServerStateManager().persistState(this.state); this.votesGranted += 1; this.votesResponded += 1; // this is the only server? if(this.votesGranted > (this.peers.size() + 1) / 2){ this.electionCompleted = true; this.becomeLeader(); return; } for(PeerServer peer : this.peers.values()){ RaftRequestMessage request = new RaftRequestMessage(); request.setMessageType(RaftMessageType.RequestVoteRequest); request.setDestination(peer.getId()); request.setSource(this.id); request.setLastLogIndex(this.logStore.getFirstAvailableIndex() - 1); request.setLastLogTerm(this.termForLastLog(this.logStore.getFirstAvailableIndex() - 1)); request.setTerm(this.state.getTerm()); this.logger.debug("send %s to server %d with term %d", RaftMessageType.RequestVoteRequest.toString(), peer.getId(), this.state.getTerm()); peer.SendRequest(request).whenCompleteAsync((RaftResponseMessage response, Throwable error) -> { handlePeerResponse(response, error); }, this.context.getScheduledExecutor()); } }