diff options
| author | Paul Buetow <paul@buetow.org> | 2023-10-04 23:07:40 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2023-10-04 23:07:40 +0300 |
| commit | 8af37a343946ee904b421cb6353ea294e31804df (patch) | |
| tree | 3f56484c89fabd5bbe1240e6109f0a9194335245 | |
| parent | 4668a883097cf437a3e8103a4ed3040db032909e (diff) | |
| parent | 36b5960e6fbf8425b550933e01b5c589d67a0c65 (diff) | |
Merge branch 'main' of codeberg.org:snonux/gorum
| -rw-r--r-- | internal/quorum/quorum.go | 97 | ||||
| -rw-r--r-- | internal/quorum/quorum_test.go | 5 |
2 files changed, 74 insertions, 28 deletions
diff --git a/internal/quorum/quorum.go b/internal/quorum/quorum.go index 04e41ce..b29b428 100644 --- a/internal/quorum/quorum.go +++ b/internal/quorum/quorum.go @@ -29,6 +29,20 @@ type Score struct { Value int } +func (s Score) Is(other Score) bool { + return s.ID == other.ID && s.Value == other.Value +} + +type Scores []Score + +func (s Scores) Winner() (Score, error) { + if len(s) == 0 { + return Score{}, fmt.Errorf("emtpy score, no winner") + } + + return s[0], nil +} + func New(conf config.Config) Quorum { return Quorum{ conf: conf, @@ -70,16 +84,9 @@ func (quo Quorum) Start(ctx context.Context) <-chan vote.Vote { return } - scoresStr, err := quo.persist() - if err != nil { + if err := quo.persist(changed); err != nil { log.Println("quorum:", err) notifyError(quo.conf, err) - continue - } - if changed { - if err := notify(quo.conf, "GORUM: Quorum changed", scoresStr); err != nil { - log.Println("quorum:", err) - } } } }() @@ -97,7 +104,7 @@ func (quo Quorum) vote(v vote.Vote) { quo.votes[v.FromID] = v } -func (quo Quorum) scores() (scores []Score) { +func (quo Quorum) scores() (scores Scores) { scoreMap := make(map[string]int) for _, vote := range quo.votes { @@ -122,17 +129,26 @@ func (quo Quorum) scores() (scores []Score) { return } -func (quo *Quorum) str() string { +func (quo *Quorum) strs() (string, string) { scores := quo.scores() log.Println("quorum scores:", scores) + winner, err := scores.Winner() + if err != nil { + log.Println("the winner is", winner.ID) + } var sb strings.Builder + winnerStr := "" + for i, score := range scores { sb.WriteString("At position ") sb.WriteString(strconv.Itoa(i + 1)) if score.ID == quo.conf.MyID { sb.WriteString(" is current node ") + if score.Is(winner) { + winnerStr = fmt.Sprintf("Yeah, I, %s, am the winner!", winner.ID) + } } else { sb.WriteString(" is partner node ") } @@ -147,36 +163,40 @@ func (quo *Quorum) str() string { sb.WriteString("\n") } - return sb.String() + return winnerStr, sb.String() } -func (quo *Quorum) persist() (string, error) { - scoresStr := quo.str() +func (quo *Quorum) persist(changed bool) error { + winnerStr, scoresStr := quo.strs() + + if changed { + if err := notify(quo.conf, "GORUM: Quorum changed", scoresStr); err != nil { + return err + } + } if _, err := os.Stat(quo.conf.StateDir); os.IsNotExist(err) { if err := os.MkdirAll(quo.conf.StateDir, 0755); err != nil { - return scoresStr, err + return err } } - stateFile := fmt.Sprintf("%s/scores", quo.conf.StateDir) - stateTmpFile := fmt.Sprintf("%s.tmp", stateFile) - - // TODO: Also create a "is_leader" marker file in the fs! - fd, err := os.Create(stateTmpFile) - if err != nil { - return scoresStr, err + if err := quo.persistWinner(winnerStr); err != nil { + return err } - defer fd.Close() - if _, err := fd.WriteString(scoresStr); err != nil { - return scoresStr, err - } + return writeFileViaTmp(fmt.Sprintf("%s/scores", quo.conf.StateDir), scoresStr) +} - if err := fd.Sync(); err != nil { - return scoresStr, err +func (quo *Quorum) persistWinner(winnerStr string) error { + winnerFile := fmt.Sprintf("%s/is_winner", quo.conf.StateDir) + if winnerStr == "" { + if _, err := os.Stat(winnerFile); err != nil { + return os.Remove(winnerFile) + } + return nil } - return scoresStr, os.Rename(stateTmpFile, stateFile) + return writeFileViaTmp(winnerFile, winnerStr) } func (quo *Quorum) makeMyVote() (vote.Vote, bool) { @@ -213,3 +233,24 @@ func (quo Quorum) expireOldVotes() (vote.Vote, error) { return vote.New(quo.conf, live...) } + +// Create tmp file first, and then, once written, rename it. +func writeFileViaTmp(filePath, content string) error { + tmpFilePath := fmt.Sprintf("%s.tmp", filePath) + + fd, err := os.Create(tmpFilePath) + if err != nil { + return err + } + defer fd.Close() + + if _, err := fd.WriteString(content); err != nil { + return err + } + + if err := fd.Sync(); err != nil { + return err + } + + return os.Rename(tmpFilePath, filePath) +} diff --git a/internal/quorum/quorum_test.go b/internal/quorum/quorum_test.go index 7ddcdb2..0a8a35c 100644 --- a/internal/quorum/quorum_test.go +++ b/internal/quorum/quorum_test.go @@ -45,6 +45,11 @@ func TestScore(t *testing.T) { if scores[0].ID != "bar" || scores[0].Value != 306 { t.Errorf("Expected score[0] to be {bar,306}: %v", scores[0]) } + + winner, _ := scores.Winner() + if winner.ID != "bar" { + t.Errorf("expectted bar to be the winner, but the winner is %s", winner.ID) + } } func TestTieScore(t *testing.T) { |
