Skip to content

Commit e7e93aa

Browse files
committed
FAB-1016 Gossip comm layer send buffering
https://jira.hyperledger.org/browse/FAB-1016 FAB-1016 Added send buffering to gossip comm layer. Now each send simply queues a message and a dedicated goroutine dispatches the sending from the queue for each remote peer. Previously, all sends were queued by spawning a goroutine that handles the send, and that didn't preserve FIFO order between peers. Change-Id: Ia34616324e28c81920ad0c31a231487ac03ae9c0 Signed-off-by: Yacov Manevich <[email protected]>
1 parent 48a117a commit e7e93aa

File tree

5 files changed

+60
-33
lines changed

5 files changed

+60
-33
lines changed

gossip/comm/comm_impl.go

+10-12
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,12 @@ import (
4040
const (
4141
defDialTimeout = time.Second * time.Duration(3)
4242
defConnTimeout = time.Second * time.Duration(2)
43-
defRecvBuffSize = 100
43+
defRecvBuffSize = 20
44+
defSendBuffSize = 20
45+
sendOverflowErr = "Send buffer overflow"
4446
)
4547

48+
var errSendOverflow = fmt.Errorf(sendOverflowErr)
4649
var dialTimeout = defDialTimeout
4750

4851
func init() {
@@ -195,7 +198,6 @@ func (c *commImpl) Send(msg *proto.GossipMessage, peers ...*RemotePeer) {
195198
c.logger.Info("Entering, sending", msg, "to ", len(peers), "peers")
196199

197200
for _, peer := range peers {
198-
// TODO: create outgoing buffers and flow control per connection
199201
go func(peer *RemotePeer, msg *proto.GossipMessage) {
200202
c.sendToEndpoint(peer, msg)
201203
}(peer, msg)
@@ -224,29 +226,25 @@ func (c *commImpl) isPKIblackListed(p PKIidType) bool {
224226
return false
225227
}
226228

227-
func (c *commImpl) sendToEndpoint(peer *RemotePeer, msg *proto.GossipMessage) error {
229+
func (c *commImpl) sendToEndpoint(peer *RemotePeer, msg *proto.GossipMessage) {
228230
if c.isStopping() {
229-
return nil
231+
return
230232
}
231233
c.logger.Debug("Entering, Sending to", peer.Endpoint, ", msg:", msg)
232234
defer c.logger.Debug("Exiting")
233235
var err error
234236

235237
conn, err := c.connStore.getConnection(peer)
236238
if err == nil {
237-
t1 := time.Now()
238-
err = conn.send(msg)
239-
if err != nil {
239+
disConnectOnErr := func(err error) {
240240
c.logger.Warning(peer, "isn't responsive:", err)
241241
c.disconnect(peer.PKIID)
242-
return err
243242
}
244-
c.logger.Debug("Send took", time.Since(t1))
245-
return nil
243+
conn.send(msg, disConnectOnErr)
244+
return
246245
}
247246
c.logger.Warning("Failed obtaining connection for", peer, "reason:", err)
248247
c.disconnect(peer.PKIID)
249-
return err
250248
}
251249

252250
func (c *commImpl) isStopping() bool {
@@ -450,7 +448,7 @@ func (c *commImpl) GossipStream(stream proto.Gossip_GossipStreamServer) error {
450448
c.connStore.closeByPKIid(PKIID)
451449
}()
452450

453-
return conn.serviceInput()
451+
return conn.serviceConnection()
454452
}
455453

456454
func (c *commImpl) Ping(context.Context, *proto.Empty) (*proto.Empty, error) {

gossip/comm/comm_test.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,15 @@ func TestHandshake(t *testing.T) {
106106
m := <-comm1.Accept(acceptAll)
107107
rcvChan <- m.GetGossipMessage()
108108
}()
109+
time.Sleep(time.Second)
109110
go stream.Send(msg2Send)
110111
time.Sleep(time.Second)
111112
assert.Equal(t, 1, len(rcvChan))
112113
var receivedMsg *proto.GossipMessage
113114
select {
114115
case receivedMsg = <-rcvChan:
115116
break
116-
case <- time.NewTicker(time.Duration(time.Second * 2)).C:
117+
case <-time.NewTicker(time.Duration(time.Second * 2)).C:
117118
assert.Fail(t, "Timed out waiting for received message")
118119
break
119120
}
@@ -255,7 +256,7 @@ func TestParallelSend(t *testing.T) {
255256
defer comm1.Stop()
256257
defer comm2.Stop()
257258

258-
messages2Send := 100
259+
messages2Send := 20
259260

260261
wg := sync.WaitGroup{}
261262
go func() {
@@ -272,13 +273,16 @@ func TestParallelSend(t *testing.T) {
272273

273274
c := 0
274275
waiting := true
275-
ticker := time.NewTicker(time.Duration(1) * time.Second)
276+
ticker := time.NewTicker(time.Duration(5) * time.Second)
276277
ch := comm2.Accept(acceptAll)
277278
for waiting {
278279
select {
279280
case <-ch:
280281
c++
281-
continue
282+
if c == messages2Send {
283+
waiting = false
284+
}
285+
break
282286
case <-ticker.C:
283287
waiting = false
284288
break
@@ -359,7 +363,7 @@ func TestAccept(t *testing.T) {
359363
comm2.Send(createGossipMsg(), &RemotePeer{Endpoint: "localhost:7611", PKIID: []byte("localhost:7611")})
360364
}
361365

362-
time.Sleep(time.Duration(1) * time.Second)
366+
time.Sleep(time.Duration(5) * time.Second)
363367

364368
comm1.Stop()
365369
comm2.Stop()

gossip/comm/conn.go

+37-12
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func (cs *connectionStore) getConnection(peer *RemotePeer) (*connection, error)
116116
conn = createdConnection
117117
cs.pki2Conn[string(createdConnection.pkiID)] = conn
118118

119-
go conn.serviceInput()
119+
go conn.serviceConnection()
120120

121121
return conn, nil
122122
}
@@ -184,6 +184,7 @@ func (cs *connectionStore) closeByPKIid(pkiID PKIidType) {
184184

185185
func newConnection(cl proto.GossipClient, c *grpc.ClientConn, cs proto.Gossip_GossipStreamClient, ss proto.Gossip_GossipStreamServer) *connection {
186186
connection := &connection{
187+
outBuff: make(chan *msgSending, defSendBuffSize),
187188
cl: cl,
188189
conn: c,
189190
clientStream: cs,
@@ -196,6 +197,7 @@ func newConnection(cl proto.GossipClient, c *grpc.ClientConn, cs proto.Gossip_Go
196197
}
197198

198199
type connection struct {
200+
outBuff chan *msgSending
199201
logger *util.Logger // logger
200202
pkiID PKIidType // pkiID of the remote endpoint
201203
handler handler // function to invoke upon a message reception
@@ -237,26 +239,24 @@ func (conn *connection) toDie() bool {
237239
return atomic.LoadInt32(&(conn.stopFlag)) == int32(1)
238240
}
239241

240-
func (conn *connection) send(msg *proto.GossipMessage) error {
242+
func (conn *connection) send(msg *proto.GossipMessage, onErr func(error)) {
241243
conn.Lock()
242244
defer conn.Unlock()
243245

244-
if conn.toDie() {
245-
return fmt.Errorf("Connection aborted")
246-
}
247-
248-
if conn.clientStream != nil {
249-
return conn.clientStream.Send(msg)
246+
if len(conn.outBuff) == defSendBuffSize {
247+
go onErr(errSendOverflow)
248+
return
250249
}
251250

252-
if conn.serverStream != nil {
253-
return conn.serverStream.Send(msg)
251+
m := &msgSending{
252+
msg: msg,
253+
onErr: onErr,
254254
}
255255

256-
return fmt.Errorf("Both streams are nil")
256+
conn.outBuff <- m
257257
}
258258

259-
func (conn *connection) serviceInput() error {
259+
func (conn *connection) serviceConnection() error {
260260
errChan := make(chan error, 1)
261261
msgChan := make(chan *proto.GossipMessage, defRecvBuffSize)
262262
defer close(msgChan)
@@ -268,6 +268,8 @@ func (conn *connection) serviceInput() error {
268268
// readFromStream() method
269269
go conn.readFromStream(errChan, msgChan)
270270

271+
go conn.writeToStream()
272+
271273
for !conn.toDie() {
272274
select {
273275
case stop := <-conn.stopChan:
@@ -283,6 +285,29 @@ func (conn *connection) serviceInput() error {
283285
return nil
284286
}
285287

288+
func (conn *connection) writeToStream() {
289+
for !conn.toDie() {
290+
stream := conn.getStream()
291+
if stream == nil {
292+
conn.logger.Error(conn.pkiID, "Stream is nil, aborting!")
293+
return
294+
}
295+
select {
296+
case m := <-conn.outBuff:
297+
err := stream.Send(m.msg)
298+
if err != nil {
299+
go m.onErr(err)
300+
return
301+
}
302+
break
303+
case stop := <-conn.stopChan:
304+
conn.logger.Warning("Closing writing to stream")
305+
conn.stopChan <- stop
306+
return
307+
}
308+
}
309+
}
310+
286311
func (conn *connection) readFromStream(errChan chan error, msgChan chan *proto.GossipMessage) {
287312
defer func() {
288313
recover()

gossip/comm/crypto.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ func generateCertificates(privKeyFile string, certKeyFile string) error {
5151

5252
sn, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
5353
template := x509.Certificate{
54-
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
55-
SerialNumber: sn,
56-
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
54+
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
55+
SerialNumber: sn,
56+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
5757
}
5858
rawBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
5959
if err != nil {

gossip/comm/msg.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type ReceivedMessageImpl struct {
3131

3232
// Respond sends a msg to the source that sent the ReceivedMessageImpl
3333
func (m *ReceivedMessageImpl) Respond(msg *proto.GossipMessage) {
34-
m.conn.send(msg)
34+
m.conn.send(msg, func(e error) {})
3535
}
3636

3737
// GetGossipMessage returns the inner GossipMessage

0 commit comments

Comments
 (0)