Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting started on a Raft Golang implementation regarding Nodes #44

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions golangraft/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ const (
Leader
)

// Node represents a node in the Raft cluster.
type Node struct {
ID int
Addr string // Address of the node for communication
}

// LogEntry represents an entry in the Raft log.
type LogEntry struct {
Term int
Expand All @@ -38,10 +32,10 @@ type Consensus struct {
}

// Assuming a list of all nodes in the cluster:
var nodes = []Node{
var nodes = []*Node{
// TODO: Add nodes here
// Example: {ID: 1, Addr: "localhost:8001"},
// {ID: 2, Addr: "localhost:8002"},
// Example: &Node{ID: 1, Addr: "localhost:8001"},
// &Node{ID: 2, Addr: "localhost:8002"},
}

func NewConsensus() *Consensus {
Expand Down
81 changes: 81 additions & 0 deletions golangraft/node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package main

import (
"sync"
"time"
)

const HeartbeatInterval = 100 * time.Millisecond

type Node struct {
mu sync.Mutex
ID int
State int
Consensus
// Channel to signal stopping of heartbeat
stopHeartbeat chan bool
}

func NewNode(id int) *Node {
return &Node{
ID: id,
State: Follower,
Consensus: *NewConsensus(),
}
}

func (n *Node) TransitionToCandidate() {
n.mu.Lock()
defer n.mu.Unlock()

n.StartElection()
if n.State != Candidate {
n.State = Candidate
}
}

func (n *Node) SendHeartbeat() {
// TODO: Implement sending of AppendEntries RPC to all followers.
}

func (n *Node) StartHeartbeatLoop() {
ticker := time.NewTicker(HeartbeatInterval)
defer ticker.Stop()

for {
select {
case <-ticker.C:
n.SendHeartbeat()
// Exit the loop when a signal is received on stopHeartbeat channel
case <-n.stopHeartbeat:
return
}
}
}

// Send a signal to stop the heartbeat loop
func (n *Node) StopHeartbeat() {
n.stopHeartbeat <- true
}

func (n *Node) TransitionToLeader() {
n.mu.Lock()
defer n.mu.Unlock()

n.becomeLeader()
if n.State != Leader {
n.State = Leader
n.stopHeartbeat = make(chan bool) // Initialize the channel
go n.StartHeartbeatLoop() // Start sending heartbeats
}
}

func (n *Node) TransitionToFollower() {
n.mu.Lock()
defer n.mu.Unlock()

if n.State == Leader {
n.StopHeartbeat() // Stop sending heartbeats if the node was previously a leader
}
n.State = Follower
}
37 changes: 37 additions & 0 deletions golangraft/node_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package main

import (
"testing"
)

func TestNewNode(t *testing.T) {
node := NewNode(1)
if node.ID != 1 {
t.Errorf("Expected node ID to be 1, got %v", node.ID)
}
if node.State != Follower {
t.Errorf("Expected initial node state to be Follower, got %v", node.State)
}
}

func TestNodeTransitions(t *testing.T) {
node := NewNode(1)

// Test transition to Candidate
node.TransitionToCandidate()
if node.State != Candidate {
t.Errorf("Expected node state to be Candidate after transition, got %v", node.State)
}

// Test transition back to Follower
node.TransitionToFollower()
if node.State != Follower {
t.Errorf("Expected node state to be Follower after transition, got %v", node.State)
}

// Test transition to Leader
node.TransitionToLeader()
if node.State != Leader {
t.Errorf("Expected node state to be Leader after transition, got %v", node.State)
}
}