Skip to content

Commit bee73bc

Browse files
committed
Make Raft.LeaderCh() return a new channel for each invocation
.. and immediately send the current leadership state over the channel. This way it can be used by multiple pieces of code with disrupting another. Sending the value immediately avoids strange race conditions when RaftState gets updated at a slightly different moment. fixes hashicorp#426
1 parent bda5e42 commit bee73bc

File tree

1 file changed

+31
-3
lines changed

1 file changed

+31
-3
lines changed

api.go

+31-3
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ type Raft struct {
111111
// leaderCh is used to notify of leadership changes
112112
leaderCh chan bool
113113

114+
leaderChs []chan bool
115+
leaderChsLock sync.Mutex
116+
// leaderChLastMessage is the last message sent over leaderCh that has been processed.
117+
// It is sent to new channels created by LeaderCh().
118+
leaderChLastMessage bool
119+
114120
// leaderState used only while state is leader
115121
leaderState leaderState
116122

@@ -957,14 +963,36 @@ func (r *Raft) State() RaftState {
957963
// lose it.
958964
//
959965
// Receivers can expect to receive a notification only if leadership
960-
// transition has occured.
966+
// transition has occured and immediately after LeaderCh() returns with the
967+
// current state.
961968
//
962969
// If receivers aren't ready for the signal, signals may drop and only the
963970
// latest leadership transition. For example, if a receiver receives subsequent
964971
// `true` values, they may deduce that leadership was lost and regained while
965-
// the the receiver was processing first leadership transition.
972+
// the receiver was processing first leadership transition.
966973
func (r *Raft) LeaderCh() <-chan bool {
967-
return r.leaderCh
974+
ch := make(chan bool, 1)
975+
r.leaderChsLock.Lock()
976+
if len(r.leaderChs) == 0 {
977+
select {
978+
case v := <-r.leaderCh:
979+
r.leaderChLastMessage = v
980+
default:
981+
}
982+
go func() {
983+
for v := range r.leaderCh {
984+
r.leaderChsLock.Lock()
985+
for _, c := range r.leaderChs {
986+
overrideNotifyBool(c, v)
987+
}
988+
r.leaderChsLock.Unlock()
989+
}
990+
}()
991+
}
992+
r.leaderChs = append(r.leaderChs, ch)
993+
ch <- r.leaderChLastMessage
994+
r.leaderChsLock.Unlock()
995+
return ch
968996
}
969997

970998
// String returns a string representation of this Raft node.

0 commit comments

Comments
 (0)