1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package tracker
16
17 import (
18 "fmt"
19 "sort"
20 "strings"
21
22 "go.etcd.io/etcd/raft/v3/quorum"
23 pb "go.etcd.io/etcd/raft/v3/raftpb"
24 )
25
26
27 type Config struct {
28 Voters quorum.JointConfig
29
30
31
32
33 AutoLeave bool
34
35
36
37
38
39
40
41
42 Learners map[uint64]struct{}
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
76
77 LearnersNext map[uint64]struct{}
78 }
79
80 func (c Config) String() string {
81 var buf strings.Builder
82 fmt.Fprintf(&buf, "voters=%s", c.Voters)
83 if c.Learners != nil {
84 fmt.Fprintf(&buf, " learners=%s", quorum.MajorityConfig(c.Learners).String())
85 }
86 if c.LearnersNext != nil {
87 fmt.Fprintf(&buf, " learners_next=%s", quorum.MajorityConfig(c.LearnersNext).String())
88 }
89 if c.AutoLeave {
90 fmt.Fprintf(&buf, " autoleave")
91 }
92 return buf.String()
93 }
94
95
96 func (c *Config) Clone() Config {
97 clone := func(m map[uint64]struct{}) map[uint64]struct{} {
98 if m == nil {
99 return nil
100 }
101 mm := make(map[uint64]struct{}, len(m))
102 for k := range m {
103 mm[k] = struct{}{}
104 }
105 return mm
106 }
107 return Config{
108 Voters: quorum.JointConfig{clone(c.Voters[0]), clone(c.Voters[1])},
109 Learners: clone(c.Learners),
110 LearnersNext: clone(c.LearnersNext),
111 }
112 }
113
114
115
116
117 type ProgressTracker struct {
118 Config
119
120 Progress ProgressMap
121
122 Votes map[uint64]bool
123
124 MaxInflight int
125 }
126
127
128 func MakeProgressTracker(maxInflight int) ProgressTracker {
129 p := ProgressTracker{
130 MaxInflight: maxInflight,
131 Config: Config{
132 Voters: quorum.JointConfig{
133 quorum.MajorityConfig{},
134 nil,
135 },
136 Learners: nil,
137 LearnersNext: nil,
138 },
139 Votes: map[uint64]bool{},
140 Progress: map[uint64]*Progress{},
141 }
142 return p
143 }
144
145
146 func (p *ProgressTracker) ConfState() pb.ConfState {
147 return pb.ConfState{
148 Voters: p.Voters[0].Slice(),
149 VotersOutgoing: p.Voters[1].Slice(),
150 Learners: quorum.MajorityConfig(p.Learners).Slice(),
151 LearnersNext: quorum.MajorityConfig(p.LearnersNext).Slice(),
152 AutoLeave: p.AutoLeave,
153 }
154 }
155
156
157
158 func (p *ProgressTracker) IsSingleton() bool {
159 return len(p.Voters[0]) == 1 && len(p.Voters[1]) == 0
160 }
161
162 type matchAckIndexer map[uint64]*Progress
163
164 var _ quorum.AckedIndexer = matchAckIndexer(nil)
165
166
167 func (l matchAckIndexer) AckedIndex(id uint64) (quorum.Index, bool) {
168 pr, ok := l[id]
169 if !ok {
170 return 0, false
171 }
172 return quorum.Index(pr.Match), true
173 }
174
175
176
177 func (p *ProgressTracker) Committed() uint64 {
178 return uint64(p.Voters.CommittedIndex(matchAckIndexer(p.Progress)))
179 }
180
181 func insertionSort(sl []uint64) {
182 a, b := 0, len(sl)
183 for i := a + 1; i < b; i++ {
184 for j := i; j > a && sl[j] < sl[j-1]; j-- {
185 sl[j], sl[j-1] = sl[j-1], sl[j]
186 }
187 }
188 }
189
190
191 func (p *ProgressTracker) Visit(f func(id uint64, pr *Progress)) {
192 n := len(p.Progress)
193
194
195
196 var sl [7]uint64
197 var ids []uint64
198 if len(sl) >= n {
199 ids = sl[:n]
200 } else {
201 ids = make([]uint64, n)
202 }
203 for id := range p.Progress {
204 n--
205 ids[n] = id
206 }
207 insertionSort(ids)
208 for _, id := range ids {
209 f(id, p.Progress[id])
210 }
211 }
212
213
214
215 func (p *ProgressTracker) QuorumActive() bool {
216 votes := map[uint64]bool{}
217 p.Visit(func(id uint64, pr *Progress) {
218 if pr.IsLearner {
219 return
220 }
221 votes[id] = pr.RecentActive
222 })
223
224 return p.Voters.VoteResult(votes) == quorum.VoteWon
225 }
226
227
228 func (p *ProgressTracker) VoterNodes() []uint64 {
229 m := p.Voters.IDs()
230 nodes := make([]uint64, 0, len(m))
231 for id := range m {
232 nodes = append(nodes, id)
233 }
234 sort.Slice(nodes, func(i, j int) bool { return nodes[i] < nodes[j] })
235 return nodes
236 }
237
238
239 func (p *ProgressTracker) LearnerNodes() []uint64 {
240 if len(p.Learners) == 0 {
241 return nil
242 }
243 nodes := make([]uint64, 0, len(p.Learners))
244 for id := range p.Learners {
245 nodes = append(nodes, id)
246 }
247 sort.Slice(nodes, func(i, j int) bool { return nodes[i] < nodes[j] })
248 return nodes
249 }
250
251
252 func (p *ProgressTracker) ResetVotes() {
253 p.Votes = map[uint64]bool{}
254 }
255
256
257
258 func (p *ProgressTracker) RecordVote(id uint64, v bool) {
259 _, ok := p.Votes[id]
260 if !ok {
261 p.Votes[id] = v
262 }
263 }
264
265
266
267 func (p *ProgressTracker) TallyVotes() (granted int, rejected int, _ quorum.VoteResult) {
268
269
270
271
272 for id, pr := range p.Progress {
273 if pr.IsLearner {
274 continue
275 }
276 v, voted := p.Votes[id]
277 if !voted {
278 continue
279 }
280 if v {
281 granted++
282 } else {
283 rejected++
284 }
285 }
286 result := p.Voters.VoteResult(p.Votes)
287 return granted, rejected, result
288 }
289
View as plain text