diff options
| author | Paul Buetow <paul@buetow.org> | 2023-06-13 00:03:17 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2023-06-13 00:03:17 +0300 |
| commit | f9aaa6b3838c336428ed1f2df30f3731e590c043 (patch) | |
| tree | 732145116fd0cc926210ebee8b8be9f689efdeac | |
| parent | fcd85ee5de82745211bdb4a1eef0077dc8a815d7 (diff) | |
refactor of Vote
| -rw-r--r-- | internal/quorum/quorum.go | 57 | ||||
| -rw-r--r-- | internal/quorum/quorum_test.go | 2 | ||||
| -rw-r--r-- | internal/vote/vote.go | 51 | ||||
| -rw-r--r-- | internal/vote/vote_test.go | 44 |
4 files changed, 69 insertions, 85 deletions
diff --git a/internal/quorum/quorum.go b/internal/quorum/quorum.go index a0306aa..febc19e 100644 --- a/internal/quorum/quorum.go +++ b/internal/quorum/quorum.go @@ -11,9 +11,11 @@ import ( ) type Quorum struct { - conf config.Config - prevLiveNodes []string + conf config.Config + // My own vote, sending to the partners + myVote vote.Vote + // From partners received votes voteCh chan vote.Vote votes map[string]vote.Vote } @@ -31,23 +33,23 @@ func New(conf config.Config) Quorum { } } -func (quo Quorum) Start(ctx context.Context) <-chan []string { - liveNodesCh := make(chan []string) +func (quo Quorum) Start(ctx context.Context) <-chan vote.Vote { + ch := make(chan vote.Vote) go func() { - defer close(liveNodesCh) + defer close(ch) for { select { case <-time.After(vote.Expiry): - if liveNodes, changed := quo.liveNodes(); changed { + if newVote, changed := quo.makeVote(); changed { quo.score() - liveNodesCh <- liveNodes + ch <- newVote } case vote := <-quo.voteCh: quo.vote(vote) - if liveNodes, changed := quo.liveNodes(); changed { - liveNodesCh <- liveNodes + if newVote, changed := quo.makeVote(); changed { + ch <- newVote } quo.score() case <-ctx.Done(): @@ -56,7 +58,7 @@ func (quo Quorum) Start(ctx context.Context) <-chan []string { } }() - return liveNodesCh + return ch } func (quo Quorum) Vote(v vote.Vote) { @@ -101,44 +103,31 @@ func (quo Quorum) score() (scores []Score) { return } -func (quo *Quorum) liveNodes() ([]string, bool) { - newLiveNodes := quo.deleteExpiredVotes() - defer func() { quo.prevLiveNodes = newLiveNodes }() - - if len(newLiveNodes) != len(quo.prevLiveNodes) { - return newLiveNodes, true - } - - for _, x := range newLiveNodes { - var found bool - for _, y := range quo.prevLiveNodes { - if x == y { - found = true - break - } - } - if !found { - return newLiveNodes, true - } +func (quo *Quorum) makeVote() (vote.Vote, bool) { + newVote, err := quo.pruneVotes() + if err != nil { + log.Println("quorum:", err) + return newVote, false } - - return newLiveNodes, false + defer func() { quo.myVote = newVote }() + return newVote, !quo.myVote.Equal(newVote) } -func (quo Quorum) deleteExpiredVotes() (liveNodes []string) { +func (quo Quorum) pruneVotes() (vote.Vote, error) { var expired []string + var live []string for fromNode, vote := range quo.votes { if vote.Expired() { expired = append(expired, fromNode) continue } - liveNodes = append(liveNodes, fromNode) + live = append(live, fromNode) } for _, e := range expired { delete(quo.votes, e) } - return + return vote.New(live) } diff --git a/internal/quorum/quorum_test.go b/internal/quorum/quorum_test.go index 8ae4043..11372ce 100644 --- a/internal/quorum/quorum_test.go +++ b/internal/quorum/quorum_test.go @@ -120,7 +120,7 @@ func TestExpire(t *testing.T) { t.Errorf("Expected to have two votes before expiry: %v", quo) } - liveNodes := quo.deleteExpiredVotes() + liveNodes := quo.pruneVotes() if len(quo.votes) != 1 { t.Errorf("Expected to have one vote after expiry: %v", quo) } diff --git a/internal/vote/vote.go b/internal/vote/vote.go index f65b21d..137beed 100644 --- a/internal/vote/vote.go +++ b/internal/vote/vote.go @@ -2,11 +2,8 @@ package vote import ( "encoding/json" - "log" - "strings" + "os" "time" - - "codeberg.org/snonux/gorum/internal/config" ) const Expiry = 20 * time.Second @@ -17,28 +14,16 @@ type Vote struct { ExpiresAt time.Time `json:"-"` } -func New(conf config.Config, message string) Vote { - var ( - fromID string - ids []string - ) - - for _, id := range strings.Split(strings.TrimSpace(message), " ") { - if !conf.IsNode(id) { - log.Println("vote: is not a node, excluding from the vote", id) - continue - } - if fromID == "" { - fromID = id - continue - } - ids = append(ids, id) +func New(ids []string) (Vote, error) { + var v Vote + hostname, err := os.Hostname() + if err != nil { + return v, err } - return Vote{ - FromID: fromID, - IDs: ids, - } + v.FromID = hostname + v.IDs = ids + return v, nil } func NewFromJSON(bytes []byte) (v Vote, err error) { @@ -58,22 +43,28 @@ func (v Vote) Expired() bool { return now.After(v.ExpiresAt) || now.Equal(v.ExpiresAt) } -func (v Vote) equals(v2 Vote) bool { - if v.FromID != v2.FromID { +func (v Vote) Equal(other Vote) bool { + if v.FromID != other.FromID { return false } - if len(v.IDs) != len(v2.IDs) { + if len(v.IDs) != len(other.IDs) { return false } - for i, id := range v.IDs { - if id != v2.IDs[i] { + for _, x := range v.IDs { + var found bool + for _, y := range other.IDs { + if x == y { + found = true + break + } + } + if !found { return false } } // Not comparing ExpiresAt - return true } diff --git a/internal/vote/vote_test.go b/internal/vote/vote_test.go index 8bc6647..04f0623 100644 --- a/internal/vote/vote_test.go +++ b/internal/vote/vote_test.go @@ -1,40 +1,44 @@ package vote import ( + "os" "testing" "time" - - "codeberg.org/snonux/gorum/internal/config" ) func TestVote(t *testing.T) { - var ( - conf = config.Config{Nodes: []string{"earth:1234", "foo:1234", "bay:4321"}} - v = New(conf, "earth foo bar baz bay\n") - ) + v, err := New([]string{"foo", "bar", "baz", "bay"}) + if err != nil { + t.Errorf(err.Error()) + } + + hostname, err := os.Hostname() + if err != nil { + t.Errorf(err.Error()) + } - if v.FromID != "earth" { + if v.FromID != hostname { t.Errorf("Expected vote to come from earth but came from %s", v.FromID) } - if len(v.IDs) != 2 { - t.Errorf("Expected vote length to be 2 but is %d", len(v.IDs)) + if len(v.IDs) != 4 { + t.Errorf("Expected vote length to be 4 but is %d", len(v.IDs)) } if v.IDs[0] != "foo" { t.Errorf("Expected vote 1 to be foo but is %s", v.IDs[0]) } - if v.IDs[1] != "bay" { - t.Errorf("Expected vote 2 to be bay but is %s", v.IDs[1]) + if v.IDs[1] != "bar" { + t.Errorf("Expected vote 2 to be bar but is %s", v.IDs[1]) } } func TestVoteExpiry(t *testing.T) { - var ( - conf = config.Config{Nodes: []string{"foo:1234", "bay:4321"}} - v = New(conf, "earth foo bar baz bay\n") - ) + v, err := New([]string{"foo", "bar", "baz", "bay"}) + if err != nil { + t.Errorf(err.Error()) + } // Set expiry 1h into the future v.ExpiresAt = time.Now().Add(1 * time.Hour) @@ -50,10 +54,10 @@ func TestVoteExpiry(t *testing.T) { } func TestMarshalling(t *testing.T) { - var ( - conf = config.Config{Nodes: []string{"foo:1234", "bay:4321"}} - v = New(conf, "earth foo bar baz bay\n") - ) + v, err := New([]string{"foo", "bar", "baz", "bay"}) + if err != nil { + t.Errorf(err.Error()) + } bytes, err := v.ToJSON() if err != nil { @@ -65,7 +69,7 @@ func TestMarshalling(t *testing.T) { t.Errorf("unable to deserialize json to vote: %v", err) } - if !v.equals(v2) { + if !v.Equal(v2) { t.Errorf("serialized %v and deserialized %v votes differ", v, v2) } } |
