summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2023-06-13 00:03:17 +0300
committerPaul Buetow <paul@buetow.org>2023-06-13 00:03:17 +0300
commitf9aaa6b3838c336428ed1f2df30f3731e590c043 (patch)
tree732145116fd0cc926210ebee8b8be9f689efdeac
parentfcd85ee5de82745211bdb4a1eef0077dc8a815d7 (diff)
refactor of Vote
-rw-r--r--internal/quorum/quorum.go57
-rw-r--r--internal/quorum/quorum_test.go2
-rw-r--r--internal/vote/vote.go51
-rw-r--r--internal/vote/vote_test.go44
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)
}
}