Browse Source

raft: use a singleton global rand

rand.NewSource creates a 4872 byte object. With a small number of raft
groups in a process this isn't a problem. With 10k raft groups we'd use
46MB for these random sources. The only usage is in
raft.resetRandomizedElectionTimeout which isn't performance critical.

Fixes #6347.
Peter Mattis 9 years ago
parent
commit
4a33aa3917
1 changed files with 22 additions and 3 deletions
  1. 22 3
      raft/raft.go

+ 22 - 3
raft/raft.go

@@ -22,6 +22,8 @@ import (
 	"math/rand"
 	"sort"
 	"strings"
+	"sync"
+	"time"
 
 	pb "github.com/coreos/etcd/raft/raftpb"
 )
@@ -45,6 +47,25 @@ const (
 	campaignTransfer CampaignType = "CampaignTransfer"
 )
 
+// lockedRand is a small wrapper around rand.Rand to provide
+// synchronization. Only the methods needed by the code are exposed
+// (e.g. Intn).
+type lockedRand struct {
+	mu   sync.Mutex
+	rand *rand.Rand
+}
+
+func (r *lockedRand) Intn(n int) int {
+	r.mu.Lock()
+	v := r.rand.Intn(n)
+	r.mu.Unlock()
+	return v
+}
+
+var globalRand = &lockedRand{
+	rand: rand.New(rand.NewSource(time.Now().UnixNano())),
+}
+
 // CampaignType represents the type of campaigning
 // the reason we use the type of string instead of uint64
 // is because it's simpler to compare and fill in raft entries
@@ -205,7 +226,6 @@ type raft struct {
 	// when raft changes its state to follower or candidate.
 	randomizedElectionTimeout int
 
-	rand *rand.Rand
 	tick func()
 	step stepFunc
 
@@ -244,7 +264,6 @@ func newRaft(c *Config) *raft {
 		logger:           c.Logger,
 		checkQuorum:      c.CheckQuorum,
 	}
-	r.rand = rand.New(rand.NewSource(int64(c.ID)))
 	for _, p := range peers {
 		r.prs[p] = &Progress{Next: 1, ins: newInflights(r.maxInflight)}
 	}
@@ -1024,7 +1043,7 @@ func (r *raft) pastElectionTimeout() bool {
 }
 
 func (r *raft) resetRandomizedElectionTimeout() {
-	r.randomizedElectionTimeout = r.electionTimeout + r.rand.Intn(r.electionTimeout)
+	r.randomizedElectionTimeout = r.electionTimeout + globalRand.Intn(r.electionTimeout)
 }
 
 // checkQuorumActive returns true if the quorum is active from