diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/client/client.go | 28 | ||||
| -rw-r--r-- | internal/config/config.go | 26 | ||||
| -rw-r--r-- | internal/config/config_test.go | 40 | ||||
| -rw-r--r-- | internal/quorum/quorum.go | 71 | ||||
| -rw-r--r-- | internal/quorum/quorum_test.go | 24 | ||||
| -rw-r--r-- | internal/run.go | 5 | ||||
| -rw-r--r-- | internal/server/tcpserver.go | 4 | ||||
| -rw-r--r-- | internal/vote/vote.go | 4 | ||||
| -rw-r--r-- | internal/vote/vote_test.go | 4 |
9 files changed, 88 insertions, 118 deletions
diff --git a/internal/client/client.go b/internal/client/client.go index 8f9e851..25d99e0 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -3,32 +3,16 @@ package client import ( "context" "log" - "time" "codeberg.org/snonux/gorum/internal/config" ) -func Start(ctx context.Context, conf config.Config, winnerCh <-chan string) { - go func() { - for { - log.Println("Starting client") - run(ctx, conf, winnerCh) - - select { - // Wait a second before restarting the clien - case <-time.After(time.Second): - case <-ctx.Done(): - return - } - } - }() -} - -func run(ctx context.Context, conf config.Config, winnerCh <-chan string) { - fanOut := make([]chan string, len(conf.Participants)) +func Start(ctx context.Context, conf config.Config, liveNodesCh <-chan []string) { + log.Println("Starting client") + fanOut := make([]chan []string, len(conf.Nodes)) for i := 0; i < len(fanOut); i++ { - fanOut[i] = make(chan string, 1) + fanOut[i] = make(chan []string, 1) } go func() { @@ -40,13 +24,13 @@ func run(ctx context.Context, conf config.Config, winnerCh <-chan string) { for { select { - case winner := <-winnerCh: + case liveNodes := <-liveNodesCh: for _, ch := range fanOut { select { case <-ch: default: } - ch <- winner + ch <- liveNodes } case <-ctx.Done(): return diff --git a/internal/config/config.go b/internal/config/config.go index 1e2705c..09302b7 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -12,9 +12,9 @@ import ( ) type Config struct { - StateDir string - Address string - Participants []string + StateDir string + Address string + Nodes []string } func New(configFile string) (Config, error) { @@ -39,21 +39,21 @@ func New(configFile string) (Config, error) { return c, nil } -func (c Config) ParticipantNumber(participant string) (int, error) { - for i, participant_ := range c.Participants { - if participant == utils.StripPort(participant_) { +func (c Config) NodeNumber(node string) (int, error) { + for i, node_ := range c.Nodes { + if node == utils.StripPort(node_) { return i, nil } } - return 0, fmt.Errorf("participant %s not found", participant) + return 0, fmt.Errorf("node %s not found", node) } -func (c Config) IsParticipant(remoteAddr string) bool { +func (c Config) IsNode(remoteAddr string) bool { remoteAddr = utils.StripPort(remoteAddr) - for _, participant := range c.Participants { - if remoteAddr == utils.StripPort(participant) { + for _, node := range c.Nodes { + if remoteAddr == utils.StripPort(node) { return true } } @@ -61,11 +61,11 @@ func (c Config) IsParticipant(remoteAddr string) bool { return false } -func (c Config) IsParticipantWithLookup(remoteAddr string, lookupIP func(string) ([]net.IP, error)) bool { +func (c Config) IsNodeWithLookup(remoteAddr string, lookupIP func(string) ([]net.IP, error)) bool { remoteAddr = utils.StripPort(remoteAddr) - for _, participant := range c.Participants { - ips, err := lookupIP(utils.StripPort(participant)) + for _, node := range c.Nodes { + ips, err := lookupIP(utils.StripPort(node)) if err != nil { log.Println(err) continue diff --git a/internal/config/config_test.go b/internal/config/config_test.go index bccfb2e..973797a 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -6,47 +6,47 @@ import ( "testing" ) -func TestParticipantNumber(t *testing.T) { - conf := Config{Participants: []string{"localhost:1234", "hamburger:4321"}} +func TestNodeNumber(t *testing.T) { + conf := Config{Nodes: []string{"localhost:1234", "hamburger:4321"}} - num, err := conf.ParticipantNumber("localhost") + num, err := conf.NodeNumber("localhost") if err != nil { t.Errorf(err.Error()) } if num != 0 { - t.Errorf("localhost should be participant number 0 but is %d", num) + t.Errorf("localhost should be node number 0 but is %d", num) } - num, err = conf.ParticipantNumber("hamburger") + num, err = conf.NodeNumber("hamburger") if err != nil { t.Errorf(err.Error()) } if num != 1 { - t.Errorf("hamburger should be participant number 1 but is %d", num) + t.Errorf("hamburger should be node number 1 but is %d", num) } - _, err = conf.ParticipantNumber("doener") + _, err = conf.NodeNumber("doener") if err == nil { - t.Errorf("doener is not a participant") + t.Errorf("doener is not a node") } } -func TestIsParticipant(t *testing.T) { - conf := Config{Participants: []string{"localhost:1234", "hamburger:4321"}} +func TestIsNode(t *testing.T) { + conf := Config{Nodes: []string{"localhost:1234", "hamburger:4321"}} remoteAddr := "localhost:323232" - if !conf.IsParticipant(remoteAddr) { - t.Errorf("%s should be participant of %v", remoteAddr, conf.Participants) + if !conf.IsNode(remoteAddr) { + t.Errorf("%s should be node of %v", remoteAddr, conf.Nodes) } remoteAddr = "foo.zone:2345" - if conf.IsParticipant(remoteAddr) { - t.Errorf("%s should not be participant of %v", remoteAddr, conf.Participants) + if conf.IsNode(remoteAddr) { + t.Errorf("%s should not be node of %v", remoteAddr, conf.Nodes) } } -func TestIsParticipantWithLookup(t *testing.T) { - conf := Config{Participants: []string{"localhost:1234", "hamburger:4321"}} +func TestIsNodeWithLookup(t *testing.T) { + conf := Config{Nodes: []string{"localhost:1234", "hamburger:4321"}} lookupIP := func(addr string) ([]net.IP, error) { switch addr { @@ -60,12 +60,12 @@ func TestIsParticipantWithLookup(t *testing.T) { } remoteAddr := "127.0.0.1:323232" - if !conf.IsParticipantWithLookup(remoteAddr, lookupIP) { - t.Errorf("%s should be participant of %v", remoteAddr, conf.Participants) + if !conf.IsNodeWithLookup(remoteAddr, lookupIP) { + t.Errorf("%s should be node of %v", remoteAddr, conf.Nodes) } remoteAddr = "9.9.9.9:2345" - if conf.IsParticipantWithLookup(remoteAddr, lookupIP) { - t.Errorf("%s should not be participant of %v", remoteAddr, conf.Participants) + if conf.IsNodeWithLookup(remoteAddr, lookupIP) { + t.Errorf("%s should not be node of %v", remoteAddr, conf.Nodes) } } diff --git a/internal/quorum/quorum.go b/internal/quorum/quorum.go index 01c861a..b299460 100644 --- a/internal/quorum/quorum.go +++ b/internal/quorum/quorum.go @@ -2,7 +2,6 @@ package quorum import ( "context" - "fmt" "log" "sort" "time" @@ -12,9 +11,9 @@ import ( ) type Quorum struct { - conf config.Config - votes map[string]vote.Vote - inCh chan vote.Vote + conf config.Config + votes map[string]vote.Vote + voteCh chan vote.Vote } type Score struct { @@ -24,54 +23,37 @@ type Score struct { func New(conf config.Config) Quorum { return Quorum{ - conf: conf, - votes: make(map[string]vote.Vote), - inCh: make(chan vote.Vote), + conf: conf, + votes: make(map[string]vote.Vote), + voteCh: make(chan vote.Vote), } } -func (quo Quorum) Start(ctx context.Context) chan string { - ch := make(chan string, 1) +func (quo Quorum) Start(ctx context.Context) <-chan []string { + liveNodesCh := make(chan []string) go func() { - defer close(ch) - var lastWinner string + defer close(liveNodesCh) for { select { case <-time.After(vote.Expiry): - quo.deleteExpiredVotes() - case vote := <-quo.inCh: + liveNodesCh <- quo.deleteExpiredVotes() + case vote := <-quo.voteCh: quo.vote(vote) - winner, err := quo.winner() - if err != nil { - log.Println(err) - continue - } - if lastWinner == winner { - continue - } - lastWinner = winner - - // Remove current channel entry, and update with new winner - select { - case <-ch: - default: - } - ch <- winner - + liveNodesCh <- quo.deleteExpiredVotes() case <-ctx.Done(): return } } }() - return ch + return liveNodesCh } func (quo Quorum) Vote(v vote.Vote) { log.Printf("Queing vote %v", v) - quo.inCh <- v + quo.voteCh <- v } func (quo Quorum) vote(v vote.Vote) { @@ -79,14 +61,6 @@ func (quo Quorum) vote(v vote.Vote) { quo.votes[v.From] = v } -func (quo Quorum) winner() (string, error) { - scores := quo.score() - if len(scores) == 0 { - return "", fmt.Errorf("unable to find a winner, empty score list") - } - return scores[0].ID, nil -} - func (quo Quorum) score() (scores []Score) { scoreMap := make(map[string]int) @@ -109,25 +83,30 @@ func (quo Quorum) score() (scores []Score) { return scores[i].Value > scores[j].Value } - // Score tie, use participant number. - i_, _ := quo.conf.ParticipantNumber(scores[i].ID) - j_, _ := quo.conf.ParticipantNumber(scores[j].ID) + // Score tie, use node number. + i_, _ := quo.conf.NodeNumber(scores[i].ID) + j_, _ := quo.conf.NodeNumber(scores[j].ID) return i_ < j_ }) + log.Println("Scores are ", scores) return } -func (quo Quorum) deleteExpiredVotes() { +func (quo Quorum) deleteExpiredVotes() (liveNodes []string) { var expired []string - for from, vote := range quo.votes { + for fromNode, vote := range quo.votes { if vote.Expired() { - expired = append(expired, from) + expired = append(expired, fromNode) + continue } + liveNodes = append(liveNodes, fromNode) } for _, e := range expired { delete(quo.votes, e) } + + return } diff --git a/internal/quorum/quorum_test.go b/internal/quorum/quorum_test.go index d0f7ec7..9953c11 100644 --- a/internal/quorum/quorum_test.go +++ b/internal/quorum/quorum_test.go @@ -9,7 +9,7 @@ import ( ) func TestScore(t *testing.T) { - conf := config.Config{Participants: []string{"foo:1234", "bar:4321", "baz:3444"}} + conf := config.Config{Nodes: []string{"foo:1234", "bar:4321", "baz:3444"}} quo := New(conf) vote1 := vote.New(conf, "foo:334234", "foo bar\n") @@ -55,7 +55,7 @@ func TestTieScore(t *testing.T) { t.Run("First tie score test", func(t *testing.T) { // If it is a tie, the first particpant (here: "foo") will win. - conf := config.Config{Participants: []string{"foo:1234", "bar:4321", "baz:3444"}} + conf := config.Config{Nodes: []string{"foo:1234", "bar:4321", "baz:3444"}} quo := New(conf) addVotes(conf, quo) @@ -68,7 +68,7 @@ func TestTieScore(t *testing.T) { t.Errorf("Expected score[0] to be {foo,3}: %v", scores[0]) } - winner, _ := quo.winner() + winner := scores[0].ID if winner != "foo" { t.Errorf("Expected the winner to be foo but is: %s", winner) } @@ -76,7 +76,7 @@ func TestTieScore(t *testing.T) { t.Run("Second tie score test", func(t *testing.T) { // If it is a tie, the first particpant (here: "bar") will win. - conf := config.Config{Participants: []string{"bar:1234", "foo:4321", "baz:3444"}} + conf := config.Config{Nodes: []string{"bar:1234", "foo:4321", "baz:3444"}} quo := New(conf) addVotes(conf, quo) @@ -89,7 +89,7 @@ func TestTieScore(t *testing.T) { t.Errorf("Expected score[0] to be {bar,3}: %v", scores[0]) } - winner, _ := quo.winner() + winner := scores[0].ID if winner != "bar" { t.Errorf("Expected the winner to be bar but is: %s", winner) } @@ -97,7 +97,7 @@ func TestTieScore(t *testing.T) { } func TestExpire(t *testing.T) { - conf := config.Config{Participants: []string{"foo:1234", "bay:4321"}} + conf := config.Config{Nodes: []string{"foo:1234", "bay:4321"}} quo := New(conf) vote1 := vote.New(conf, "foo:334234", " foo bar baz bay\n") @@ -112,8 +112,16 @@ func TestExpire(t *testing.T) { t.Errorf("Expected to have two votes before expiry: %v", quo) } - quo.deleteExpiredVotes() + liveNodes := quo.deleteExpiredVotes() if len(quo.votes) != 1 { - t.Errorf("Expected to have only one vote after expiry: %v", quo) + t.Errorf("Expected to have one vote after expiry: %v", quo) + } + + if len(liveNodes) != 1 { + t.Errorf("Expected to have one live node after expiry: %v", quo) + } + + if liveNodes[0] != "foo" { + t.Errorf("Expected 'foo' to be the live node, but got : %v", liveNodes[0]) } } diff --git a/internal/run.go b/internal/run.go index 08fe8dc..9b9df1d 100644 --- a/internal/run.go +++ b/internal/run.go @@ -16,10 +16,9 @@ func Run(ctx context.Context, configFile string) error { } quo := quorum.New(conf) - winnerCh := quo.Start(ctx) - + liveNodesCh := quo.Start(ctx) server.Start(ctx, conf, quo) - client.Start(ctx, conf, winnerCh) + client.Start(ctx, conf, liveNodesCh) return nil } diff --git a/internal/server/tcpserver.go b/internal/server/tcpserver.go index 4cb5b02..9ebbb60 100644 --- a/internal/server/tcpserver.go +++ b/internal/server/tcpserver.go @@ -29,8 +29,8 @@ func tcpServerRun(ctx context.Context, conf config.Config, continue } - if !conf.IsParticipantWithLookup(conn.RemoteAddr().String(), net.LookupIP) { - log.Printf("Denying connection, peer not a participant: %v\n", conn.RemoteAddr().String()) + if !conf.IsNodeWithLookup(conn.RemoteAddr().String(), net.LookupIP) { + log.Printf("Denying connection, peer not a node: %v\n", conn.RemoteAddr().String()) conn.Close() continue } diff --git a/internal/vote/vote.go b/internal/vote/vote.go index 079e021..75fa67a 100644 --- a/internal/vote/vote.go +++ b/internal/vote/vote.go @@ -20,8 +20,8 @@ type Vote struct { func New(conf config.Config, from, message string) Vote { var ids []string for _, id := range strings.Split(strings.TrimSpace(message), " ") { - if !conf.IsParticipant(id) { - log.Printf("%s is not a participant, excluding from the vote", id) + if !conf.IsNode(id) { + log.Printf("%s is not a node, excluding from the vote", id) continue } ids = append(ids, id) diff --git a/internal/vote/vote_test.go b/internal/vote/vote_test.go index 0986e00..af8b741 100644 --- a/internal/vote/vote_test.go +++ b/internal/vote/vote_test.go @@ -8,7 +8,7 @@ import ( ) func TestVote(t *testing.T) { - conf := config.Config{Participants: []string{"foo:1234", "bay:4321"}} + conf := config.Config{Nodes: []string{"foo:1234", "bay:4321"}} v := New(conf, "earth:334234", " foo bar baz bay\n") if v.From != "earth" { @@ -29,7 +29,7 @@ func TestVote(t *testing.T) { } func TestVoteExpiry(t *testing.T) { - conf := config.Config{Participants: []string{"foo:1234", "bay:4321"}} + conf := config.Config{Nodes: []string{"foo:1234", "bay:4321"}} v := New(conf, "earth:334234", " foo bar baz bay\n") // Set expiry 1h into the future |
