Example #1
0
func leaderFunc(pid int, rserver *rServer, channelClose chan bool, inbox chan *cluster.Envelope, outbox chan *cluster.Envelope, peers []int, debug bool) {
	// var nextIndex = make([]int, len(peers))
	// var matchIndex = make([]int, len(peers))
innerl:
	for {
		select {
		case <-time.After(time.Millisecond * heartbeatInterval):
			// Add the PrevLogTerm in the appendEntries message
			var PrevLogTerm int = DEFAULT_TERM
			// Read Previous logEntry to get the PrevLogTerm
			rserver.ReaderRequest <- &ReadRequest{Index: rserver.CommitIndex - 1}
			// Wait for the logEntry to be read
			select {
			case serverLogEntry := <-rserver.ReaderData:
				if debug {
					log.Printf("Leader %d read logEntry %v", rserver.Pid, serverLogEntry)
				}
				if serverLogEntry != nil {
					PrevLogTerm = serverLogEntry.ServerLogData.Term
				}
			}
			appendEntries := AppendEntries{Term: rserver.CurrentTerm, LeaderId: pid, PrevLogIndex: rserver.CommitIndex - 1, PrevLogTerm: PrevLogTerm, Entries: nil, LeaderCommit: rserver.CommitIndex}
			for peer := range peers {
				peerId := peers[peer]
				if debug {
					log.Printf("Leader %d with term %d Sending alive message to server %d", pid, rserver.CurrentTerm, peerId)
				}
				outbox <- &cluster.Envelope{Pid: peerId, MsgId: int64(rserver.CurrentTerm), Msg: appendEntries}
			}
		case message := <-inbox:
			switch msg := message.Msg.(type) {
			// If a server with higher term comes up then step down as a follwer
			case AppendEntries:
				if msg.Term > rserver.CurrentTerm {
					if debug {
						log.Printf("Server %d with term %d is changing state from leader to follower", pid, rserver.CurrentTerm)
					}
					rserver.RSLeader = false
					rserver.State = FOLLOWER
					inbox <- message
					break innerl
				}
			case AppendEntriesStatus:
				if debug {
					log.Printf("leader server %d with term %d Received status message from %d with term %d", pid, rserver.CurrentTerm, message.Pid, msg.Term)
				}
				if msg.Term >= rserver.CurrentTerm {
					if debug {
						log.Printf("Server %d with term %d is changing state from leader to follower", pid, rserver.CurrentTerm)
					}
					rserver.RSLeader = false
					rserver.State = FOLLOWER
					break innerl
				}
			case RequestVote:
				if msg.Term >= rserver.CurrentTerm && msg.Term != rserver.VotedFor {
					if debug {
						log.Printf("Leader %d with term %d is changing state from leader to follower", pid, rserver.CurrentTerm)
					}
					if debug {
						log.Printf("Leader %d with term %d is voting for server %d with term %d", pid, rserver.CurrentTerm, message.Pid, message.MsgId)
					}
					outbox <- &cluster.Envelope{Pid: message.Pid, MsgId: int64(rserver.CurrentTerm), Msg: ElectionStatusMessage{Term: rserver.CurrentTerm, Granted: true}}
					rserver.RSLeader = false
					rserver.State = FOLLOWER
					rserver.VotedFor = msg.Term
					break innerl
				} else {
					outbox <- &cluster.Envelope{Pid: message.Pid, MsgId: int64(rserver.CurrentTerm), Msg: ElectionStatusMessage{Term: rserver.CurrentTerm, Granted: false}}
				}
			}
		// Received command from client
		case message := <-rserver.Outer:
			// Create a new Log Entry
			if debug {
				log.Printf("Received message %v", message)
			}
			logEntry := serverLogEntry{Index: rserver.CommitIndex + 1, ServerLogData: serverLogData{Term: rserver.CurrentTerm, Command: message}}
			if debug {
				log.Printf("CommitIndex of leader is %d", rserver.CommitIndex)
				log.Printf("Created logEntry (%v) for message", logEntry)
			}
			rserver.Writer <- &logEntry

			// Used to determine the success of follower commits
			var count = 0

			// Wait for the write to be successful
			select {
			case status := <-rserver.WriteSuccess:
				if status {
					count = count + 1
					if debug {
						log.Printf("Write succesful")
					}
				}
			}
			// Add the PrevLogTerm in the appendEntries message
			var PrevLogTerm int
			// Read Previous logEntry to ensure that it is in sync with the leader
			rserver.ReaderRequest <- &ReadRequest{Index: rserver.CommitIndex}
			// Wait for the logEntry to be read
			select {
			case serverLogEntry := <-rserver.ReaderData:
				if debug {
					log.Printf("Leader %d read logEntry %v", rserver.Pid, serverLogEntry)
				}
				if serverLogEntry != nil {
					PrevLogTerm = serverLogEntry.ServerLogData.Term
				}
			}
			// Now replicate this logEntry on all the followers
			for peer := range peers {
				peerId := peers[peer]
				entries := []serverLogEntry{logEntry}
				if debug {
					log.Printf("Leader %d with term %d is sending entries to follower %d", pid, rserver.CurrentTerm, peerId)
				}
				appendEntries := AppendEntries{Term: rserver.CurrentTerm, LeaderId: pid, PrevLogIndex: rserver.CommitIndex, PrevLogTerm: PrevLogTerm, Entries: entries, LeaderCommit: rserver.CommitIndex}
				outbox <- &cluster.Envelope{Pid: peerId, MsgId: int64(rserver.CurrentTerm), Msg: appendEntries}
			}

			// Wait for write success on majority
		checkCommit:
			for {
				select {
				case message := <-inbox:
					switch msg := message.Msg.(type) {
					case AppendEntriesStatus:
						if msg.Term > rserver.CurrentTerm {
							if debug {
								log.Printf("Server %d with term %d is changing state from leader to follower", pid, rserver.CurrentTerm)
							}
						} else {
							if msg.Success {
								if count < cluster.Count() {
									count = count + 1
									// Majority of followers were able to log
									if count >= cluster.Count()/2 {
										// The commit was successful on all the followers
										// Tell the client that the command has been written to the log with the index given in logEntry
										if debug {
											log.Printf("Sending success of write to client")
										}
										// Create a Log Entry for the Client
										clogEntry := LogEntry{Index: logEntry.Index, Command: logEntry.ServerLogData.Command}
										rserver.Inner <- &clogEntry
										break checkCommit
									}
								}
							}
						}
					case RequestVote:
						if msg.Term >= rserver.CurrentTerm && msg.Term != rserver.VotedFor {
							if debug {
								log.Printf("Leader %d with term %d is changing state from leader to follower", pid, rserver.CurrentTerm)
							}
							if debug {
								log.Printf("Leader %d with term %d is voting for server %d with term %d", pid, rserver.CurrentTerm, message.Pid, message.MsgId)
							}
							outbox <- &cluster.Envelope{Pid: message.Pid, MsgId: int64(rserver.CurrentTerm), Msg: ElectionStatusMessage{Term: rserver.CurrentTerm, Granted: true}}
							rserver.RSLeader = false
							rserver.State = FOLLOWER
							rserver.VotedFor = msg.Term
							break innerl
						} else {
							outbox <- &cluster.Envelope{Pid: message.Pid, MsgId: int64(rserver.CurrentTerm), Msg: ElectionStatusMessage{Term: rserver.CurrentTerm, Granted: false}}
						}
					case AppendEntries:
						if msg.Term > rserver.CurrentTerm {
							if debug {
								log.Printf("Leader %d with term %d is changing state from leader to follower", pid, rserver.CurrentTerm)
							}
							rserver.RSLeader = false
							rserver.State = FOLLOWER
							inbox <- message
							break innerl
						}
					}
					// Retry after waiting for sometime
					// case time.After(time.Second):
					// break checkCommit
				}
			}
			// Increment the commitIndex
			if debug {
				log.Printf("Incrementing commitIndex to %d", rserver.CommitIndex+1)
			}
			rserver.CommitIndex = rserver.CommitIndex + 1
			appendEntries := AppendEntries{Term: rserver.CurrentTerm, LeaderId: pid, PrevLogIndex: rserver.CommitIndex, PrevLogTerm: PrevLogTerm, Entries: nil, LeaderCommit: rserver.CommitIndex}
			for peer := range peers {
				peerId := peers[peer]
				if debug {
					log.Printf("Leader %d with term %d Sending alive message to server %d", pid, rserver.CurrentTerm, peerId)
				}
				outbox <- &cluster.Envelope{Pid: peerId, MsgId: int64(rserver.CurrentTerm), Msg: appendEntries}
			}
			kvCommand := logEntry.ServerLogData.Command
			switch cmd := kvCommand.(type) {
			case KVCommand:
				switch cmd.Command {
				case PUT:
					rserver.StateMachine[cmd.Key] = cmd.Value
				case DEL:
					delete(rserver.StateMachine, cmd.Key)
				case GET:
					// No action needs to be performed for GET
				}
			}
			rserver.LastApplied = rserver.CommitIndex
		case <-channelClose:
			rserver.State = EXIT
			break innerl
		}
	}
}
Example #2
0
func candidateFunc(pid int, rserver *rServer, channelClose chan bool, inbox chan *cluster.Envelope, outbox chan *cluster.Envelope, peers []int, debug bool) {
	var LastLogTerm int
	// Read Previous logEntry to ensure that it is in sync with the leader
	rserver.ReaderRequest <- &ReadRequest{Index: rserver.CommitIndex}
	// Wait for the logEntry to be read
	select {
	case serverLogEntry := <-rserver.ReaderData:
		if debug {
			log.Printf("Candidate %d read logEntry %v", rserver.Pid, serverLogEntry)
		}
		if serverLogEntry != nil {
			LastLogTerm = serverLogEntry.ServerLogData.Term
		} else {
			LastLogTerm = DEFAULT_TERM
		}
	}
	requestVote := RequestVote{Term: rserver.CurrentTerm, CandidateId: pid, LastLogIndex: rserver.CommitIndex, LastLogTerm: LastLogTerm}
	// Send request to become a leader to all the peers
	for peer := range peers {
		peerId := peers[peer]
		if debug {
			log.Printf("Server %d with term %d is sending request message to server %d", pid, rserver.CurrentTerm, peerId)
		}
		outbox <- &cluster.Envelope{Pid: peerId, MsgId: int64(rserver.CurrentTerm), Msg: requestVote}
	}
	// It will now wait for response from all the peers
	var count int = 1 // Assume server votes for itself
innerc:
	for {
		select {
		case message := <-inbox:
			switch msg := message.Msg.(type) {
			case ElectionStatusMessage:
				if msg.Granted {
					if debug {
						log.Printf("server %d with term %d got vote from server %d with term %d", pid, rserver.CurrentTerm, message.Pid, message.MsgId)
					}
					count++
					if count > cluster.Count()/2 {
						// The candidate has won the election
						// It should now act a leader and send alive messages to everyone
						if debug {
							log.Printf("server %d with term %d has now become a leader", pid, rserver.CurrentTerm)
						}
						rserver.CurrentTerm++
						rserver.State = LEADER
						rserver.RSLeader = true
						break innerc
					}
				}
			case AppendEntries:
				// A new leader was elected
				if msg.Term >= rserver.CurrentTerm {
					// Change state to follower
					if debug {
						log.Printf("Server %d with term %d is changing state from candidate to follower", pid, rserver.CurrentTerm)
					}
					rserver.State = FOLLOWER
					inbox <- message
					break innerc
				}
			case RequestVote:
				if msg.Term >= rserver.CurrentTerm && msg.Term > rserver.VotedFor {
					if debug {
						log.Printf("Server %d with term %d is changing state from candidate to follower", pid, rserver.CurrentTerm)
					}
					if debug {
						log.Printf("server %d with term %d is voting for server %d with term %d", pid, rserver.CurrentTerm, message.Pid, message.MsgId)
					}
					outbox <- &cluster.Envelope{Pid: message.Pid, MsgId: int64(rserver.CurrentTerm), Msg: ElectionStatusMessage{Term: rserver.CurrentTerm, Granted: true}}
					rserver.State = FOLLOWER
					rserver.VotedFor = msg.Term
					break innerc
				} else {
					outbox <- &cluster.Envelope{Pid: message.Pid, MsgId: int64(rserver.CurrentTerm), Msg: ElectionStatusMessage{Term: rserver.CurrentTerm, Granted: false}}
				}
			case LogEntry:
				// This message is ignored by candidates at the moment
			}
		case <-time.After(time.Duration(int(time.Millisecond) * rserver.Timeout)):
			rserver.State = CANDIDATE
			rserver.CurrentTerm++
		case <-channelClose:
			rserver.State = EXIT
			break innerc
		}
	}
}