Internal API cleanup, vote timing jitter
This commit is contained in:
18
candidate.go
18
candidate.go
@@ -20,8 +20,8 @@ type Candidate struct {
|
|||||||
|
|
||||||
numVoters int
|
numVoters int
|
||||||
signingKey []byte
|
signingKey []byte
|
||||||
stop chan<- bool
|
stop chan bool
|
||||||
done <-chan bool
|
done chan bool
|
||||||
resp voteResponse
|
resp voteResponse
|
||||||
c chan<- CandidateState
|
c chan<- CandidateState
|
||||||
|
|
||||||
@@ -38,8 +38,6 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func NewCandidate(numVoters int, signingKey string) *Candidate {
|
func NewCandidate(numVoters int, signingKey string) *Candidate {
|
||||||
stop := make(chan bool)
|
|
||||||
done := make(chan bool)
|
|
||||||
change := make(chan CandidateState, 100)
|
change := make(chan CandidateState, 100)
|
||||||
|
|
||||||
c := &Candidate{
|
c := &Candidate{
|
||||||
@@ -47,15 +45,15 @@ func NewCandidate(numVoters int, signingKey string) *Candidate {
|
|||||||
numVoters: numVoters,
|
numVoters: numVoters,
|
||||||
signingKey: []byte(signingKey),
|
signingKey: []byte(signingKey),
|
||||||
votes: map[string]*vote{},
|
votes: map[string]*vote{},
|
||||||
stop: stop,
|
stop: make(chan bool),
|
||||||
done: done,
|
done: make(chan bool),
|
||||||
c: change,
|
c: change,
|
||||||
resp: voteResponse{
|
resp: voteResponse{
|
||||||
CandidateID: uniuri.New(),
|
CandidateID: uniuri.New(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
go c.loop(stop, done)
|
go c.loop()
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@@ -237,14 +235,14 @@ func (c *Candidate) update(state CandidateState) {
|
|||||||
c.c <- state
|
c.c <- state
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Candidate) loop(stop <-chan bool, done chan<- bool) {
|
func (c *Candidate) loop() {
|
||||||
t := time.NewTicker(1 * time.Second)
|
t := time.NewTicker(1 * time.Second)
|
||||||
defer t.Stop()
|
defer t.Stop()
|
||||||
defer close(done)
|
defer close(c.done)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-stop:
|
case <-c.stop:
|
||||||
return
|
return
|
||||||
|
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
|
|||||||
51
voter.go
51
voter.go
@@ -4,6 +4,7 @@ import (
|
|||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dchest/uniuri"
|
"github.com/dchest/uniuri"
|
||||||
@@ -12,12 +13,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Voter struct {
|
type Voter struct {
|
||||||
|
// used by user and loop() goroutines
|
||||||
|
update chan time.Duration
|
||||||
|
done chan bool
|
||||||
|
|
||||||
|
// used by loop() goroutine only
|
||||||
client *resty.Client
|
client *resty.Client
|
||||||
signingKey []byte
|
signingKey []byte
|
||||||
update chan<- time.Duration
|
|
||||||
done <-chan bool
|
|
||||||
vote vote
|
vote vote
|
||||||
candidates []*Candidate
|
candidates []*Candidate
|
||||||
|
period time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
type vote struct {
|
type vote struct {
|
||||||
@@ -36,22 +41,20 @@ type voteResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewVoter(url string, signingKey string) *Voter {
|
func NewVoter(url string, signingKey string) *Voter {
|
||||||
update := make(chan time.Duration)
|
|
||||||
done := make(chan bool)
|
|
||||||
|
|
||||||
v := &Voter{
|
v := &Voter{
|
||||||
client: resty.New().
|
client: resty.New().
|
||||||
SetCloseConnection(true).
|
SetCloseConnection(true).
|
||||||
SetBaseURL(url),
|
SetBaseURL(url),
|
||||||
signingKey: []byte(signingKey),
|
signingKey: []byte(signingKey),
|
||||||
update: update,
|
update: make(chan time.Duration),
|
||||||
done: done,
|
done: make(chan bool),
|
||||||
vote: vote{
|
vote: vote{
|
||||||
VoterID: uniuri.New(),
|
VoterID: uniuri.New(),
|
||||||
},
|
},
|
||||||
|
period: 5 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
go v.loop(update, done)
|
go v.loop()
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
@@ -65,40 +68,42 @@ func (v *Voter) AddCandidate(c *Candidate) {
|
|||||||
v.candidates = append(v.candidates, c)
|
v.candidates = append(v.candidates, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Voter) loop(update <-chan time.Duration, done chan<- bool) {
|
func (v *Voter) loop() {
|
||||||
// TODO: Need a JitterTicker
|
defer close(v.done)
|
||||||
t := time.NewTicker(5 * time.Second)
|
|
||||||
defer t.Stop()
|
|
||||||
defer close(done)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !v.poll(update, t) {
|
if !v.poll() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Voter) poll(update <-chan time.Duration, t *time.Ticker) bool {
|
func (v *Voter) poll() bool {
|
||||||
|
// mean: v.period, max: v.period*2
|
||||||
|
t := time.NewTimer(randDurationN(v.period * 2))
|
||||||
|
defer t.Stop()
|
||||||
|
|
||||||
t2 := &time.Timer{}
|
t2 := &time.Timer{}
|
||||||
|
|
||||||
if v.vote.NumPollsSinceChange <= 10 {
|
if v.vote.NumPollsSinceChange <= 10 {
|
||||||
t2 = time.NewTimer(100 * time.Millisecond)
|
// mean: 100ms, max: 200ms
|
||||||
|
t2 = time.NewTimer(randDurationN(100 * time.Millisecond * 2))
|
||||||
defer t2.Stop()
|
defer t2.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-t2.C:
|
|
||||||
v.sendVote()
|
|
||||||
|
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
v.sendVote()
|
v.sendVote()
|
||||||
|
|
||||||
case period, ok := <-update:
|
case <-t2.C:
|
||||||
|
v.sendVote()
|
||||||
|
|
||||||
|
case period, ok := <-v.update:
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Reset(period)
|
v.period = period
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -169,3 +174,7 @@ func (v *Voter) sendVote() {
|
|||||||
func (v *Voter) log(format string, args ...any) {
|
func (v *Voter) log(format string, args ...any) {
|
||||||
log.Printf("[voter] "+format, args...)
|
log.Printf("[voter] "+format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func randDurationN(n time.Duration) time.Duration {
|
||||||
|
return time.Duration(rand.Int63n(int64(n))) //nolint:gosec
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user