summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2023-08-06 20:12:22 +0300
committerPaul Buetow <paul@buetow.org>2023-08-06 20:12:22 +0300
commit36b5960e6fbf8425b550933e01b5c589d67a0c65 (patch)
treea3885147b7223e12222d7959df38b22d16b92b14 /internal
parentbaa44274ced7476e0243b30f93493cb1c06a9b40 (diff)
add is_winner winner marker
Diffstat (limited to 'internal')
-rw-r--r--internal/quorum/quorum.go97
-rw-r--r--internal/quorum/quorum_test.go5
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) {