Skip to content

Commit d1d3428

Browse files
authored
Merge pull request #143 from relab/decouple-node-from-channel
Decouple node from channel
2 parents a5ad159 + f685e26 commit d1d3428

File tree

3 files changed

+77
-57
lines changed

3 files changed

+77
-57
lines changed

channel.go

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/relab/gorums/ordering"
1212
"google.golang.org/grpc"
13+
"google.golang.org/grpc/backoff"
1314
"google.golang.org/grpc/codes"
1415
"google.golang.org/grpc/status"
1516
"google.golang.org/protobuf/reflect/protoreflect"
@@ -29,7 +30,11 @@ type response struct {
2930

3031
type channel struct {
3132
sendQ chan request
32-
node *Node // needed for ID and setLastError
33+
nodeID uint32
34+
mu sync.Mutex
35+
lastError error
36+
latency time.Duration
37+
backoffCfg backoff.Config
3338
rand *rand.Rand
3439
gorumsClient ordering.GorumsClient
3540
gorumsStream ordering.Gorums_NodeStreamClient
@@ -45,7 +50,9 @@ type channel struct {
4550
func newChannel(n *Node) *channel {
4651
return &channel{
4752
sendQ: make(chan request, n.mgr.opts.sendBuffer),
48-
node: n,
53+
backoffCfg: n.mgr.opts.backoff,
54+
nodeID: n.ID(),
55+
latency: -1 * time.Second,
4956
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
5057
responseRouter: make(map[uint64]chan<- response),
5158
}
@@ -113,7 +120,7 @@ func (c *channel) sendMsg(req request) (err error) {
113120

114121
err = c.gorumsStream.SendMsg(req.msg)
115122
if err != nil {
116-
c.node.setLastErr(err)
123+
c.setLastErr(err)
117124
c.streamBroken.set()
118125
}
119126
done <- struct{}{}
@@ -132,14 +139,14 @@ func (c *channel) sendMsgs() {
132139
// return error if stream is broken
133140
if c.streamBroken.get() {
134141
err := status.Errorf(codes.Unavailable, "stream is down")
135-
c.routeResponse(req.msg.Metadata.MessageID, response{nid: c.node.ID(), msg: nil, err: err})
142+
c.routeResponse(req.msg.Metadata.MessageID, response{nid: c.nodeID, msg: nil, err: err})
136143
continue
137144
}
138145
// else try to send message
139146
err := c.sendMsg(req)
140147
if err != nil {
141148
// return the error
142-
c.routeResponse(req.msg.Metadata.MessageID, response{nid: c.node.ID(), msg: nil, err: err})
149+
c.routeResponse(req.msg.Metadata.MessageID, response{nid: c.nodeID, msg: nil, err: err})
143150
}
144151
}
145152
}
@@ -152,13 +159,13 @@ func (c *channel) recvMsgs() {
152159
if err != nil {
153160
c.streamBroken.set()
154161
c.streamMut.RUnlock()
155-
c.node.setLastErr(err)
162+
c.setLastErr(err)
156163
// attempt to reconnect
157164
c.reconnect()
158165
} else {
159166
c.streamMut.RUnlock()
160167
err := status.FromProto(resp.Metadata.GetStatus()).Err()
161-
c.routeResponse(resp.Metadata.MessageID, response{nid: c.node.ID(), msg: resp.Message, err: err})
168+
c.routeResponse(resp.Metadata.MessageID, response{nid: c.nodeID, msg: resp.Message, err: err})
162169
}
163170

164171
select {
@@ -172,7 +179,7 @@ func (c *channel) recvMsgs() {
172179
func (c *channel) reconnect() {
173180
c.streamMut.Lock()
174181
defer c.streamMut.Unlock()
175-
backoffCfg := c.node.mgr.opts.backoff
182+
backoffCfg := c.backoffCfg
176183

177184
var retries float64
178185
for {
@@ -185,7 +192,7 @@ func (c *channel) reconnect() {
185192
return
186193
}
187194
c.cancelStream()
188-
c.node.setLastErr(err)
195+
c.setLastErr(err)
189196
delay := float64(backoffCfg.BaseDelay)
190197
max := float64(backoffCfg.MaxDelay)
191198
for r := retries; delay < max && r > 0; r-- {
@@ -202,6 +209,26 @@ func (c *channel) reconnect() {
202209
}
203210
}
204211

212+
func (c *channel) setLastErr(err error) {
213+
c.mu.Lock()
214+
defer c.mu.Unlock()
215+
c.lastError = err
216+
}
217+
218+
// lastErr returns the last error encountered (if any) when using this channel.
219+
func (c *channel) lastErr() error {
220+
c.mu.Lock()
221+
defer c.mu.Unlock()
222+
return c.lastError
223+
}
224+
225+
// channelLatency returns the latency between the client and this channel.
226+
func (c *channel) channelLatency() time.Duration {
227+
c.mu.Lock()
228+
defer c.mu.Unlock()
229+
return c.latency
230+
}
231+
205232
type atomicFlag struct {
206233
flag int32
207234
}

node.go

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"net"
88
"sort"
99
"strconv"
10-
"sync"
1110
"time"
1211

1312
"google.golang.org/grpc"
@@ -20,14 +19,11 @@ const nilAngleString = "<nil>"
2019
// can be performed.
2120
type Node struct {
2221
// Only assigned at creation.
23-
id uint32
24-
addr string
25-
conn *grpc.ClientConn
26-
cancel func()
27-
mu sync.Mutex
28-
lastErr error
29-
latency time.Duration
30-
mgr *Manager
22+
id uint32
23+
addr string
24+
conn *grpc.ClientConn
25+
cancel func()
26+
mgr *Manager
3127

3228
// the default channel
3329
channel *channel
@@ -42,9 +38,8 @@ func NewNode(addr string) (*Node, error) {
4238
h := fnv.New32a()
4339
_, _ = h.Write([]byte(tcpAddr.String()))
4440
return &Node{
45-
id: h.Sum32(),
46-
addr: tcpAddr.String(),
47-
latency: -1 * time.Second,
41+
id: h.Sum32(),
42+
addr: tcpAddr.String(),
4843
}, nil
4944
}
5045

@@ -55,9 +50,8 @@ func NewNodeWithID(addr string, id uint32) (*Node, error) {
5550
return nil, fmt.Errorf("node error: '%s' error: %v", addr, err)
5651
}
5752
return &Node{
58-
id: id,
59-
addr: tcpAddr.String(),
60-
latency: -1 * time.Second,
53+
id: id,
54+
addr: tcpAddr.String(),
6155
}, nil
6256
}
6357

@@ -136,28 +130,19 @@ func (n *Node) String() string {
136130
// includes id, network address and latency information.
137131
func (n *Node) FullString() string {
138132
if n != nil {
139-
n.mu.Lock()
140-
defer n.mu.Unlock()
141-
return fmt.Sprintf(
142-
"node %d | addr: %s | latency: %v",
143-
n.id, n.addr, n.latency,
144-
)
133+
return fmt.Sprintf("node %d | addr: %s", n.id, n.addr)
145134
}
146135
return nilAngleString
147136
}
148137

149-
func (n *Node) setLastErr(err error) {
150-
n.mu.Lock()
151-
defer n.mu.Unlock()
152-
n.lastErr = err
138+
// LastErr returns the last error encountered (if any) for this node.
139+
func (n *Node) LastErr() error {
140+
return n.channel.lastErr()
153141
}
154142

155-
// LastErr returns the last error encountered (if any) when invoking a remote
156-
// procedure call on this node.
157-
func (n *Node) LastErr() error {
158-
n.mu.Lock()
159-
defer n.mu.Unlock()
160-
return n.lastErr
143+
// Latency returns the latency between the client and this node.
144+
func (n *Node) Latency() time.Duration {
145+
return n.channel.channelLatency()
161146
}
162147

163148
type lessFunc func(n1, n2 *Node) bool
@@ -194,8 +179,8 @@ func (ms *MultiSorter) Swap(i, j int) {
194179
}
195180

196181
// Less is part of sort.Interface. It is implemented by looping along the
197-
// less functions until it finds a comparison that is either Less or
198-
// !Less. Note that it can call the less functions twice per call. We
182+
// less functions until it finds a comparison that is either Less or not
183+
// Less. Note that it can call the less functions twice per call. We
199184
// could change the functions to return -1, 0, 1 and reduce the
200185
// number of calls for greater efficiency: an exercise for the reader.
201186
func (ms *MultiSorter) Less(i, j int) bool {
@@ -235,7 +220,7 @@ var Port = func(n1, n2 *Node) bool {
235220
// LastNodeError sorts nodes by their LastErr() status in increasing order. A
236221
// node with LastErr() != nil is larger than a node with LastErr() == nil.
237222
var LastNodeError = func(n1, n2 *Node) bool {
238-
if n1.lastErr != nil && n2.lastErr == nil {
223+
if n1.channel.lastErr() != nil && n2.channel.lastErr() == nil {
239224
return false
240225
}
241226
return true

node_test.go

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,32 @@ import (
1010
func TestNodeSort(t *testing.T) {
1111
nodes := []*Node{
1212
{
13-
id: 100,
14-
lastErr: nil,
15-
latency: time.Second,
13+
id: 100,
14+
channel: &channel{
15+
lastError: nil,
16+
latency: time.Second,
17+
},
1618
},
1719
{
18-
id: 101,
19-
lastErr: errors.New("some error"),
20-
latency: 250 * time.Millisecond,
20+
id: 101,
21+
channel: &channel{
22+
lastError: errors.New("some error"),
23+
latency: 250 * time.Millisecond,
24+
},
2125
},
2226
{
23-
id: 42,
24-
lastErr: nil,
25-
latency: 300 * time.Millisecond,
27+
id: 42,
28+
channel: &channel{
29+
lastError: nil,
30+
latency: 300 * time.Millisecond,
31+
},
2632
},
2733
{
28-
id: 99,
29-
lastErr: errors.New("some error"),
30-
latency: 500 * time.Millisecond,
34+
id: 99,
35+
channel: &channel{
36+
lastError: errors.New("some error"),
37+
latency: 500 * time.Millisecond,
38+
},
3139
},
3240
}
3341

@@ -43,7 +51,7 @@ func TestNodeSort(t *testing.T) {
4351

4452
OrderedBy(LastNodeError).Sort(nodes)
4553
for i := n - 1; i > 0; i-- {
46-
if nodes[i].lastErr == nil && nodes[i-1].lastErr != nil {
54+
if nodes[i].LastErr() == nil && nodes[i-1].LastErr() != nil {
4755
t.Error("by error: not sorted")
4856
printNodes(t, nodes)
4957
}
@@ -55,7 +63,7 @@ func printNodes(t *testing.T, nodes []*Node) {
5563
for i, n := range nodes {
5664
nodeStr := fmt.Sprintf(
5765
"%d: node %d | addr: %s | latency: %v | err: %v",
58-
i, n.id, n.addr, n.latency, n.lastErr)
66+
i, n.id, n.addr, n.Latency(), n.LastErr())
5967
t.Logf("%s", nodeStr)
6068
}
6169
}

0 commit comments

Comments
 (0)