For my work I need to make some modifications to an in-house raft library (https://raft.github.io/). Without delving too deep into the algorithm, one of the stages in the algorithm is a "candidate" goroutine that performs an assessment of whether it should be able to transition to a "leader" state.
There are some rules around how the candidate performs this assessment and one of the rules is to periodically run an "election" where the candidate seeks votes from its peers and if it receives the majority of votes, it declares itself a leader. The algorithm is a bit more nuanced to this but for the sake of simplification I am focusing just on the part where the election proceeds as follows: 1. Starts the election process. 2. Sets an election timer, which periodically expires, if the candidate has not received any votes during that period, it will reset the deadline and start the election all over again. With that in mind I modeled my implementation as follows: func (f *candidate) startElection(ctx context.Context) <-chan struct{} { //Channel that signals to the caller when the election is done electionCompletionChan := make(chan struct{}) go func() { //Instead of busy-looping, running the poll logic at predefined intervals ticker := time.NewTicker(config.PollDuration * time.Second) defer ticker.Stop() //Reset the election deadline. SetDeadline takes the current time and sets a random offset //in the future f.raftTimer.SetDeadline(time.Now()) //callElection makes the RPC to its peers requesting votes and upon completion, //returns its status to the voteStatusChan channel voteStatusChan := f.callElection() //Polling for tick := range ticker.C { select { //The caller called cancel on the context case <-ctx.Done(): log.Println("Cancelling election") //Send an empty struct signaling the end of the election process electionCompletionChan <- struct{}{} return //Checking whether all the votes have been received case voteStatus := <-voteStatusChan: { switch voteStatus { //Majority of peers agreed that the candidate should be the leader case voter.Leader: //Flip the state to leader f.leaderTrigger <- LeaderTrigger{} //Conclude the election process electionCompletionChan <- struct{}{} return default: //Split vote or loss: Do nothing, re-election will be triggered } } default: //Check whether the election deadline was exceeded if tick.After(f.raftTimer.GetDeadline()) { //Reset the deadline f.raftTimer.SetDeadline(tick) //Once again make the callElection to redo the election voteStatusChan = f.callElection() } } } }() return electionCompletionChan } There are a few things that I am worried about in the above given snippet. The first thing is whether I am in alignment with golang's idioms while modeling this process using channels and the second thing is where I am resetting the voteStatusChan by creating channels within the poll loop. Something about creating new channels for every tick of the timer seems to be wasteful (I don't have a particularly nuanced understanding of how unbounded channel creations will tax the garbage collector or if there are other dangers lurking in the corner by taking this approach) It will be great to get some feedback on what I may be doing wrong here. Thanks -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/c860d984-9c24-478a-b1fc-c554efcf264fn%40googlegroups.com.