-
Notifications
You must be signed in to change notification settings - Fork 57
/
leaner.go
75 lines (65 loc) · 1.75 KB
/
leaner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package paxos
import (
"log"
"time"
)
type learner struct {
id int
acceptors map[int]accept
nt network
}
func newLearner(id int, nt network, acceptors ...int) *learner {
l := &learner{id: id, nt: nt, acceptors: make(map[int]accept)}
for _, a := range acceptors {
l.acceptors[a] = message{typ: Accept}
}
return l
}
// A value is learned when a single proposal with that value has been accepted by
// a majority of the acceptors.
func (l *learner) learn() string {
for {
m, ok := l.nt.recv(time.Hour)
if !ok {
continue
}
if m.typ != Accept {
log.Panicf("learner: %d received unexpected msg %+v", l.id, m)
}
l.receiveAccepted(m)
accept, ok := l.chosen()
if !ok {
continue
}
log.Printf("learner: %d learned the chosen propose %+v", l.id, accept)
return accept.proposalValue()
}
}
func (l *learner) receiveAccepted(accepted message) {
a := l.acceptors[accepted.from]
if a.proposalNumber() < accepted.n {
log.Printf("learner: %d received a new accepted proposal %+v", l.id, accepted)
l.acceptors[accepted.from] = accepted
}
}
func (l *learner) majority() int { return len(l.acceptors)/2 + 1 }
// A proposal is chosen when it has been accepted by a majority of the
// acceptors.
// The leader might choose multiple proposals when it learns multiple times,
// but we guarantee that all chosen proposals have the same value.
func (l *learner) chosen() (accept, bool) {
counts := make(map[int]int)
accepteds := make(map[int]accept)
for _, accepted := range l.acceptors {
if accepted.proposalNumber() != 0 {
counts[accepted.proposalNumber()]++
accepteds[accepted.proposalNumber()] = accepted
}
}
for n, count := range counts {
if count >= l.majority() {
return accepteds[n], true
}
}
return message{}, false
}