Skip to content

Commit 8a9a2a1

Browse files
authored
Merge pull request etcd-io#10309 from tbg/fix/raft-status-allocs
raft: add (*RawNode).WithProgress
2 parents bfa2c15 + bd332b3 commit 8a9a2a1

File tree

3 files changed

+123
-13
lines changed

3 files changed

+123
-13
lines changed

raft/rawnode.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,39 @@ func (rn *RawNode) Status() *Status {
236236
return &status
237237
}
238238

239+
// StatusWithoutProgress returns a Status without populating the Progress field
240+
// (and returns the Status as a value to avoid forcing it onto the heap). This
241+
// is more performant if the Progress is not required. See WithProgress for an
242+
// allocation-free way to introspect the Progress.
243+
func (rn *RawNode) StatusWithoutProgress() Status {
244+
return getStatusWithoutProgress(rn.raft)
245+
}
246+
247+
// ProgressType indicates the type of replica a Progress corresponds to.
248+
type ProgressType byte
249+
250+
const (
251+
// ProgressTypePeer accompanies a Progress for a regular peer replica.
252+
ProgressTypePeer ProgressType = iota
253+
// ProgressTypeLearner accompanies a Progress for a learner replica.
254+
ProgressTypeLearner
255+
)
256+
257+
// WithProgress is a helper to introspect the Progress for this node and its
258+
// peers.
259+
func (rn *RawNode) WithProgress(visitor func(id uint64, typ ProgressType, pr Progress)) {
260+
for id, pr := range rn.raft.prs {
261+
pr := *pr
262+
pr.ins = nil
263+
visitor(id, ProgressTypePeer, pr)
264+
}
265+
for id, pr := range rn.raft.learnerPrs {
266+
pr := *pr
267+
pr.ins = nil
268+
visitor(id, ProgressTypeLearner, pr)
269+
}
270+
}
271+
239272
// ReportUnreachable reports the given node is not reachable for the last send.
240273
func (rn *RawNode) ReportUnreachable(id uint64) {
241274
_ = rn.raft.Step(pb.Message{Type: pb.MsgUnreachable, From: id})

raft/rawnode_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package raft
1616

1717
import (
1818
"bytes"
19+
"fmt"
1920
"reflect"
2021
"testing"
2122

@@ -545,3 +546,73 @@ func TestRawNodeBoundedLogGrowthWithPartition(t *testing.T) {
545546
rawNode.Advance(rd)
546547
checkUncommitted(0)
547548
}
549+
550+
func BenchmarkStatusProgress(b *testing.B) {
551+
setup := func(members int) *RawNode {
552+
peers := make([]uint64, members)
553+
for i := range peers {
554+
peers[i] = uint64(i + 1)
555+
}
556+
cfg := newTestConfig(1, peers, 3, 1, NewMemoryStorage())
557+
cfg.Logger = discardLogger
558+
r := newRaft(cfg)
559+
r.becomeFollower(1, 1)
560+
r.becomeCandidate()
561+
r.becomeLeader()
562+
return &RawNode{raft: r}
563+
}
564+
565+
for _, members := range []int{1, 3, 5, 100} {
566+
b.Run(fmt.Sprintf("members=%d", members), func(b *testing.B) {
567+
// NB: call getStatus through rn.Status because that incurs an additional
568+
// allocation.
569+
rn := setup(members)
570+
571+
b.Run("Status", func(b *testing.B) {
572+
b.ReportAllocs()
573+
for i := 0; i < b.N; i++ {
574+
_ = rn.Status()
575+
}
576+
})
577+
578+
b.Run("Status-example", func(b *testing.B) {
579+
b.ReportAllocs()
580+
for i := 0; i < b.N; i++ {
581+
s := rn.Status()
582+
var n uint64
583+
for _, pr := range s.Progress {
584+
n += pr.Match
585+
}
586+
_ = n
587+
}
588+
})
589+
590+
b.Run("StatusWithoutProgress", func(b *testing.B) {
591+
b.ReportAllocs()
592+
for i := 0; i < b.N; i++ {
593+
_ = rn.StatusWithoutProgress()
594+
}
595+
})
596+
597+
b.Run("WithProgress", func(b *testing.B) {
598+
b.ReportAllocs()
599+
visit := func(uint64, ProgressType, Progress) {}
600+
601+
for i := 0; i < b.N; i++ {
602+
rn.WithProgress(visit)
603+
}
604+
})
605+
b.Run("WithProgress-example", func(b *testing.B) {
606+
b.ReportAllocs()
607+
for i := 0; i < b.N; i++ {
608+
var n uint64
609+
visit := func(_ uint64, _ ProgressType, pr Progress) {
610+
n += pr.Match
611+
}
612+
rn.WithProgress(visit)
613+
_ = n
614+
}
615+
})
616+
})
617+
}
618+
}

raft/status.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,35 @@ type Status struct {
3232
LeadTransferee uint64
3333
}
3434

35-
// getStatus gets a copy of the current raft status.
36-
func getStatus(r *raft) Status {
35+
func getProgressCopy(r *raft) map[uint64]Progress {
36+
prs := make(map[uint64]Progress)
37+
for id, p := range r.prs {
38+
prs[id] = *p
39+
}
40+
41+
for id, p := range r.learnerPrs {
42+
prs[id] = *p
43+
}
44+
return prs
45+
}
46+
47+
func getStatusWithoutProgress(r *raft) Status {
3748
s := Status{
3849
ID: r.id,
3950
LeadTransferee: r.leadTransferee,
4051
}
41-
4252
s.HardState = r.hardState()
4353
s.SoftState = *r.softState()
44-
4554
s.Applied = r.raftLog.applied
55+
return s
56+
}
4657

58+
// getStatus gets a copy of the current raft status.
59+
func getStatus(r *raft) Status {
60+
s := getStatusWithoutProgress(r)
4761
if s.RaftState == StateLeader {
48-
s.Progress = make(map[uint64]Progress)
49-
for id, p := range r.prs {
50-
s.Progress[id] = *p
51-
}
52-
53-
for id, p := range r.learnerPrs {
54-
s.Progress[id] = *p
55-
}
62+
s.Progress = getProgressCopy(r)
5663
}
57-
5864
return s
5965
}
6066

0 commit comments

Comments
 (0)