/
golem.go
284 lines (229 loc) · 5.03 KB
/
golem.go
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
package golem
import (
"Time"
"uuid"
zmq "github.com/alecthomas/gozmq"
)
type timestamp int
type status_code int
type message_id string
type Node struct {
id string
host string
port int
to chan Outgoing
open_requests map[string](*chan bool)
status status_code
last_heard_from Time
}
type Signature struct {
signer *Node
ts timestamp
}
type OpenRequest struct {
id string
name string
msg string
done bool
sigs []Signature
}
//Incoming
//TODO rename to question and answer? or something
type Request struct {
ts int
id string
from *Node
name string
msg string
sigs []Signature
}
type Response struct {
ts int
id string
from *Node
sigs []Signature
}
type Ping struct {
ts int
id string
from *Node
sigs []Signature
}
type Ack struct {
ts int
}
type Incoming interface {
Decode([]byte) (error)
}
type Outgoing interface {
Encode() ([]byte, error)
}
const (
DEFAULT_HOST = '*'
DEFAULT_LISTEN_PORT = '52252'
DEFAULT_PUB_PORT = '5222'
DEFAULT_SUB_PORT = '5333'
NEEDED_OKS = 2
status_code NODE_UP = 0
status_code NODE_DOWN = 1
status_code NODE_DEAD = 2
)
type Golem struct {
host string
port int
cluster_time Time
me string
wake_up chan bool
known_nodes map[string]*Node
open_request_queue OpenRequestQueue
zcontext zmq.zmqContext
}
func Instance() {
g = Golem.new()
g.cluster_time = Time.now()
g.me = uuid.GenUUID()
g.wake_up = make(chan int)
g.known_nodes = make(map[string]*Node)
g.request_queue = RequestQueue.new()
g.zcontext, _ = zmq.NewContext()
}
func NewNode(id string, host string, port int) *Node {
node = new(Node)
node.id = string
node.host = host
node.port = port
node.to = make(chan Outgoing)
node.open_requests = make(map[string](*chan bool))
//TODO move these out
known_nodes[id] = node
go node.Monitor()
return node
}
func (node *Node) SendRequest(r Request, success *chan bool) {
node.to<-r
//would some other multiplexing model suit better?
response := make(chan bool)
node.open_requests[r.id] = response
//wait for said response
<-response
success<-true
}
func (g *Golem) MonitorNode(node *Node) {
socket := gzcontext.Socket(zmq.REQ)
socket.Connect(fmt.Sprintf("tcp://%s:%d", node.host, node.port))
socket.setSockOptString(zmq.IDENTITY, me)
for req := range node.to {
socket.Send(req.Encode(), 0)
//TODO time out, freak out
resp, _ := socket.Recv(0)
ack = Ack.new()
_ = ack.Decode(resp)
//TODO error handling
//update timestamp state
}
}
func (g *Golem) DisenfranchiseNode(node *Node) {
//stuff to do when we freak out:
//set state to NODE_DOWN
//close any open requests
// node down event? (for locks, to release open ones)
}
func (g *Golem) AskRandomNode(request r, success *chan bool) {
//TODO pick neighbor
node := nil
if node == nil {
return false
}
r = NewRequest(r, node)
go node.SendRequest(r, success)
return true
}
func (g *Golem) HandleRequest(r Request) {
//update timestamp state, gossip
or := OpenRequest.new()
or.sigs = make([]Signature)
copy(r.sigs, or.sigs)
action_queue.Push(or)
oks, success := 0, make(chan bool)
for i := NEEDED_OKS {
if !AskRandomNeighbor(r, success) {
break
}
}
for s := range success {
if s {
oks += 1
if oks >= NEEDED_OKS {
break
}
} else {
//pick another neighbor and try again
if !g.AskRandomNeighbor(r, success) {
//not enough neigbors. break
break
}
}
}
r.done = true
wake_up<-true
}
func (g *Golem) HandleResponse(r Response) {
//update timestamp state, gossip
//copy sigs on to open_request
//update last_heard_from on nodes (from sigs)
if success, present := r.from.open_requests[r.id]; present {
delete(r.from.open_requests, r.id)
success<-true
} else {
//freak out, or something, I donno
}
}
func (g *Golem) Listen() {
socket = g.zcontext.Socket(zmq.REP)
socket.Connect(fmt.Sprintf("tcp://%s:%d", host, port))
socket.setSockOptString(zmq.IDENTITY, me)
for {
//TODO error handling
msg, _ := socket.Recv(0)
socket.Send(newAck(), 0)
switch msg_type = DetermineMessageType(msg); msg_type {
case "request":
req := Request.Decode(msg)
//update node status and time
HandleRequest(req)
case "ping":
//update node status and time
case "bye"
//what is a bye?
//todo
}
}
}
func Log() {
}
func (g *Golem) start() {
//start logger
go g.Log()
go g.Listen()
//wait for a request to be OK'd. Once it has, pop as many OK'd requests as we can
//but only execute them if we have a quorum. otherwise, re-open them
for range wake_up {
for {
if !g.request_queue.peek().done {
break
}
req := g.request_queue.pop()
//check for quorum
if len(req.sigs)*2 <= len(g.known_hosts) {
req.Reopen()
continue
}
req.action.Execute()
}
}
}
//other todos
//logging/verbosity
//encode and decode
//action interface
// -serialize, deserialize, execute