From 4a33aa3917eca1a2431ab2e6bd72047f8a659ba7 Mon Sep 17 00:00:00 2001 From: Peter Mattis Date: Mon, 5 Sep 2016 09:03:18 -0400 Subject: [PATCH] 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. --- raft/raft.go | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/raft/raft.go b/raft/raft.go index bc1a94db3..f236281e7 100644 --- a/raft/raft.go +++ b/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