-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathraft_node.go
More file actions
148 lines (112 loc) · 3.13 KB
/
Copy pathraft_node.go
File metadata and controls
148 lines (112 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package raft
import (
"fmt"
"log"
"os"
"strconv"
"sync"
"time"
)
const HeartbeatLogs = false
const VoteRequestLogs = true
type LogEntry struct {
Command interface{}
Term int
}
// Main Raft Data Structure
type RaftNode struct {
mu sync.Mutex
id int
peersIds []int
// Persistent state on all servers
currentTerm int
votedFor int
log []LogEntry
// Volatile state on all servers
commitIndex int
lastApplied int
// Volatile Raft state on leaders
nextIndex map[int]int
matchIndex map[int]int
// Utility States
state string
lastElectionTimerStartedTime time.Time
notifyToApplyCommit chan int
LOG_ENTRIES bool
filePath string
// Networking Component
server *Server
}
// Constructor for RaftNodes
func NewRaftNode(id int, peersIds []int, server *Server, ready <-chan interface{}) *RaftNode {
this := new(RaftNode)
this.server = server
this.notifyToApplyCommit = make(chan int, 16)
this.id = id
this.peersIds = peersIds
this.votedFor = -1
this.currentTerm = 0
this.commitIndex = -1
this.lastApplied = -1
this.nextIndex = make(map[int]int)
this.matchIndex = make(map[int]int)
this.state = "Follower"
this.LOG_ENTRIES = true
this.filePath = "NodeLogs/" + strconv.Itoa(this.id)
f, _ := os.Create(this.filePath)
f.Close()
go func() {
// Signalled when all servers are up and running, ready to receive RPCs
<-ready
this.mu.Lock()
this.lastElectionTimerStartedTime = time.Now()
this.mu.Unlock()
this.startElectionTimer()
}()
go this.applyCommitedLogEntries() // Fire off watcher to apply any committed entries
return this
}
// This function implements the 'application' of a query to the leader
// This is the function that also writes queries accepted by the leader to files
// to observe as output
func (this *RaftNode) applyCommitedLogEntries() {
for range this.notifyToApplyCommit {
this.mu.Lock()
var entriesToApply []LogEntry
if this.commitIndex > this.lastApplied {
entriesToApply = this.log[this.lastApplied+1 : this.commitIndex+1]
}
f, _ := os.OpenFile(this.filePath, os.O_APPEND|os.O_WRONLY, 0644)
defer f.Close()
for i, entry := range entriesToApply {
strentry := fmt.Sprintf("%s; T:[%d]; I:[%d]", entry.Command, this.currentTerm, this.commitIndex+i)
f.WriteString(strentry)
f.WriteString("\n")
}
this.lastApplied = this.commitIndex
this.mu.Unlock()
}
this.write_log("applyCommitedLogEntries done")
}
/* UTILITY FUNCTIONS */
// GetNodeState reports the state of this RN.
func (this *RaftNode) GetNodeState() (id int, term int, isLeader bool) {
this.mu.Lock()
defer this.mu.Unlock()
return this.id, this.currentTerm, this.state == "Leader"
}
// Kills a RaftNode and sets its state to Dead
func (this *RaftNode) KillNode() {
this.mu.Lock()
defer this.mu.Unlock()
this.state = "Dead"
this.write_log("KILLED")
close(this.notifyToApplyCommit)
}
// This function logs all messages to the terminal
func (this *RaftNode) write_log(format string, args ...interface{}) {
if this.LOG_ENTRIES {
format = fmt.Sprintf("AT NODE %d: ", this.id) + format
log.Printf(format, args...)
}
}