@Override public synchronized ServerState readState() { try{ if(this.serverStateFile.length() == 0){ return null; } byte[] stateData = new byte[Long.BYTES * 2 + Integer.BYTES]; this.read(stateData); this.serverStateFile.seek(0); ByteBuffer buffer = ByteBuffer.wrap(stateData); ServerState state = new ServerState(); state.setTerm(buffer.getLong()); state.setCommitIndex(buffer.getLong()); state.setVotedFor(buffer.getInt()); return state; }catch(IOException ioError){ this.logger.error("failed to read from the server state file", ioError); throw new RuntimeException("fatal I/O error while reading from state file", ioError); } }
@Override public synchronized void persistState(ServerState serverState) { try{ ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * 2 + Integer.BYTES); buffer.putLong(serverState.getTerm()); buffer.putLong(serverState.getCommitIndex()); buffer.putInt(serverState.getVotedFor()); this.serverStateFile.write(buffer.array()); this.serverStateFile.seek(0); }catch(IOException ioError){ this.logger.error("failed to write to the server state file", ioError); throw new RuntimeException("fatal I/O error while writing to the state file", ioError); } }
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; }
response.setSource(this.id); response.setDestination(request.getSource()); response.setTerm(this.state.getTerm()); response.setMessageType(RaftMessageType.JoinClusterResponse); response.setNextIndex(this.logStore.getFirstAvailableIndex()); this.role = ServerRole.Follower; this.leader = request.getSource(); this.state.setTerm(request.getTerm()); this.state.setCommitIndex(0); this.quickCommitIndex = 0; this.state.setVotedFor(-1); this.context.getServerStateManager().persistState(this.state); this.stopElectionTimer(); ClusterConfiguration newConfig = ClusterConfiguration.fromBytes(logEntries[0].getValue()); this.reconfigure(newConfig); response.setTerm(this.state.getTerm()); response.setAccepted(true); return response;
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 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; }
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()); } }
while(true){ try{ long currentCommitIndex = server.state.getCommitIndex(); while(server.quickCommitIndex <= currentCommitIndex || currentCommitIndex >= server.logStore.getFirstAvailableIndex() - 1){ currentCommitIndex = server.state.getCommitIndex(); server.state.setCommitIndex(currentCommitIndex); server.snapshotAndCompact(currentCommitIndex);
this.state.increaseTerm(); this.state.setVotedFor(-1); this.role = ServerRole.Candidate; this.votesGranted = 0;
private void commit(long targetIndex){ if(targetIndex > this.quickCommitIndex){ this.quickCommitIndex = targetIndex; // if this is a leader notify peers to commit as well // for peers that are free, send the request, otherwise, set pending commit flag for that peer if(this.role == ServerRole.Leader){ for(PeerServer peer : this.peers.values()){ if(!this.requestAppendEntries(peer)){ peer.setPendingCommit(); } } } } if(this.logStore.getFirstAvailableIndex() - 1 > this.state.getCommitIndex() && this.quickCommitIndex > this.state.getCommitIndex()){ this.commitingThread.moreToCommit(); } }
this.state.setCommitIndex(snapshotSyncRequest.getSnapshot().getLastLogIndex()); this.quickCommitIndex = snapshotSyncRequest.getSnapshot().getLastLogIndex(); this.context.getServerStateManager().persistState(this.state);
response.setSource(this.id); response.setDestination(request.getSource()); response.setTerm(this.state.getTerm()); response.setMessageType(RaftMessageType.JoinClusterResponse); response.setNextIndex(this.logStore.getFirstAvailableIndex()); this.role = ServerRole.Follower; this.leader = request.getSource(); this.state.setTerm(request.getTerm()); this.state.setCommitIndex(0); this.quickCommitIndex = 0; this.state.setVotedFor(-1); this.context.getServerStateManager().persistState(this.state); this.stopElectionTimer(); ClusterConfiguration newConfig = ClusterConfiguration.fromBytes(logEntries[0].getValue()); this.reconfigure(newConfig); response.setTerm(this.state.getTerm()); response.setAccepted(true); return response;
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 boolean updateTerm(long term){ if(term > this.state.getTerm()){ this.state.setTerm(term); this.state.setVotedFor(-1); this.electionCompleted = false; this.votesGranted = 0; this.votesResponded = 0; this.context.getServerStateManager().persistState(this.state); this.becomeFollower(); return true; } return false; }
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; }
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()); } }
while(true){ try{ long currentCommitIndex = server.state.getCommitIndex(); while(server.quickCommitIndex <= currentCommitIndex || currentCommitIndex >= server.logStore.getFirstAvailableIndex() - 1){ currentCommitIndex = server.state.getCommitIndex(); server.state.setCommitIndex(currentCommitIndex); server.snapshotAndCompact(currentCommitIndex);
this.state.increaseTerm(); this.state.setVotedFor(-1); this.role = ServerRole.Candidate; this.votedServers.clear();
private void commit(long targetIndex){ if(targetIndex > this.quickCommitIndex){ this.quickCommitIndex = targetIndex; // if this is a leader notify peers to commit as well // for peers that are free, send the request, otherwise, set pending commit flag for that peer if(this.role == ServerRole.Leader){ for(PeerServer peer : this.peers.values()){ if(!this.requestAppendEntries(peer)){ peer.setPendingCommit(); } } } } if(this.logStore.getFirstAvailableIndex() - 1 > this.state.getCommitIndex() && this.quickCommitIndex > this.state.getCommitIndex()){ this.commitingThread.moreToCommit(); } }
this.state.setCommitIndex(snapshotSyncRequest.getSnapshot().getLastLogIndex()); this.quickCommitIndex = snapshotSyncRequest.getSnapshot().getLastLogIndex(); this.context.getServerStateManager().persistState(this.state);