github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/gossip/comm/conn.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package comm
    18  
    19  import (
    20  	"errors"
    21  	"sync"
    22  	"sync/atomic"
    23  
    24  	"github.com/hyperledger/fabric/gossip/common"
    25  	"github.com/hyperledger/fabric/gossip/util"
    26  	proto "github.com/hyperledger/fabric/protos/gossip"
    27  	"github.com/op/go-logging"
    28  	"google.golang.org/grpc"
    29  )
    30  
    31  type handler func(message *proto.SignedGossipMessage)
    32  
    33  type connFactory interface {
    34  	createConnection(endpoint string, pkiID common.PKIidType) (*connection, error)
    35  }
    36  
    37  type connectionStore struct {
    38  	logger           *logging.Logger          // logger
    39  	isClosing        bool                     // whether this connection store is shutting down
    40  	connFactory      connFactory              // creates a connection to remote peer
    41  	sync.RWMutex                              // synchronize access to shared variables
    42  	pki2Conn         map[string]*connection   // mapping between pkiID to connections
    43  	destinationLocks map[string]*sync.RWMutex //mapping between pkiIDs and locks,
    44  	// used to prevent concurrent connection establishment to the same remote endpoint
    45  }
    46  
    47  func newConnStore(connFactory connFactory, logger *logging.Logger) *connectionStore {
    48  	return &connectionStore{
    49  		connFactory:      connFactory,
    50  		isClosing:        false,
    51  		pki2Conn:         make(map[string]*connection),
    52  		destinationLocks: make(map[string]*sync.RWMutex),
    53  		logger:           logger,
    54  	}
    55  }
    56  
    57  func (cs *connectionStore) getConnection(peer *RemotePeer) (*connection, error) {
    58  	cs.RLock()
    59  	isClosing := cs.isClosing
    60  	cs.RUnlock()
    61  
    62  	if isClosing {
    63  		return nil, errors.New("Shutting down")
    64  	}
    65  
    66  	pkiID := peer.PKIID
    67  	endpoint := peer.Endpoint
    68  
    69  	cs.Lock()
    70  	destinationLock, hasConnected := cs.destinationLocks[string(pkiID)]
    71  	if !hasConnected {
    72  		destinationLock = &sync.RWMutex{}
    73  		cs.destinationLocks[string(pkiID)] = destinationLock
    74  	}
    75  	cs.Unlock()
    76  
    77  	destinationLock.Lock()
    78  
    79  	cs.RLock()
    80  	conn, exists := cs.pki2Conn[string(pkiID)]
    81  	if exists {
    82  		cs.RUnlock()
    83  		destinationLock.Unlock()
    84  		return conn, nil
    85  	}
    86  	cs.RUnlock()
    87  
    88  	createdConnection, err := cs.connFactory.createConnection(endpoint, pkiID)
    89  
    90  	destinationLock.Unlock()
    91  
    92  	if cs.isClosing {
    93  		return nil, errors.New("ConnStore is closing")
    94  	}
    95  
    96  	cs.Lock()
    97  	delete(cs.destinationLocks, string(pkiID))
    98  	defer cs.Unlock()
    99  
   100  	// check again, maybe someone connected to us during the connection creation?
   101  	conn, exists = cs.pki2Conn[string(pkiID)]
   102  
   103  	if exists {
   104  		if createdConnection != nil {
   105  			createdConnection.close()
   106  		}
   107  		return conn, nil
   108  	}
   109  
   110  	// no one connected to us AND we failed connecting!
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	// at this point in the code, we created a connection to a remote peer
   116  	conn = createdConnection
   117  	cs.pki2Conn[string(createdConnection.pkiID)] = conn
   118  
   119  	go conn.serviceConnection()
   120  
   121  	return conn, nil
   122  }
   123  
   124  func (cs *connectionStore) connNum() int {
   125  	cs.RLock()
   126  	defer cs.RUnlock()
   127  	return len(cs.pki2Conn)
   128  }
   129  
   130  func (cs *connectionStore) closeConn(peer *RemotePeer) {
   131  	cs.Lock()
   132  	defer cs.Unlock()
   133  
   134  	if conn, exists := cs.pki2Conn[string(peer.PKIID)]; exists {
   135  		conn.close()
   136  		delete(cs.pki2Conn, string(conn.pkiID))
   137  	}
   138  }
   139  
   140  func (cs *connectionStore) shutdown() {
   141  	cs.Lock()
   142  	cs.isClosing = true
   143  	pkiIds2conn := cs.pki2Conn
   144  	cs.Unlock()
   145  
   146  	wg := sync.WaitGroup{}
   147  	for _, conn := range pkiIds2conn {
   148  		wg.Add(1)
   149  		go func(conn *connection) {
   150  			cs.closeByPKIid(conn.pkiID)
   151  			wg.Done()
   152  		}(conn)
   153  	}
   154  	wg.Wait()
   155  }
   156  
   157  func (cs *connectionStore) onConnected(serverStream proto.Gossip_GossipStreamServer, pkiID common.PKIidType) *connection {
   158  	cs.Lock()
   159  	defer cs.Unlock()
   160  
   161  	if c, exists := cs.pki2Conn[string(pkiID)]; exists {
   162  		c.close()
   163  	}
   164  
   165  	return cs.registerConn(pkiID, serverStream)
   166  }
   167  
   168  func (cs *connectionStore) registerConn(pkiID common.PKIidType, serverStream proto.Gossip_GossipStreamServer) *connection {
   169  	conn := newConnection(nil, nil, nil, serverStream)
   170  	conn.pkiID = pkiID
   171  	conn.logger = cs.logger
   172  	cs.pki2Conn[string(pkiID)] = conn
   173  	return conn
   174  }
   175  
   176  func (cs *connectionStore) closeByPKIid(pkiID common.PKIidType) {
   177  	cs.Lock()
   178  	defer cs.Unlock()
   179  	if conn, exists := cs.pki2Conn[string(pkiID)]; exists {
   180  		conn.close()
   181  		delete(cs.pki2Conn, string(pkiID))
   182  	}
   183  }
   184  
   185  func newConnection(cl proto.GossipClient, c *grpc.ClientConn, cs proto.Gossip_GossipStreamClient, ss proto.Gossip_GossipStreamServer) *connection {
   186  	connection := &connection{
   187  		outBuff:      make(chan *msgSending, util.GetIntOrDefault("peer.gossip.sendBuffSize", defSendBuffSize)),
   188  		cl:           cl,
   189  		conn:         c,
   190  		clientStream: cs,
   191  		serverStream: ss,
   192  		stopFlag:     int32(0),
   193  		stopChan:     make(chan struct{}, 1),
   194  	}
   195  
   196  	return connection
   197  }
   198  
   199  type connection struct {
   200  	outBuff      chan *msgSending
   201  	logger       *logging.Logger                 // logger
   202  	pkiID        common.PKIidType                // pkiID of the remote endpoint
   203  	handler      handler                         // function to invoke upon a message reception
   204  	conn         *grpc.ClientConn                // gRPC connection to remote endpoint
   205  	cl           proto.GossipClient              // gRPC stub of remote endpoint
   206  	clientStream proto.Gossip_GossipStreamClient // client-side stream to remote endpoint
   207  	serverStream proto.Gossip_GossipStreamServer // server-side stream to remote endpoint
   208  	stopFlag     int32                           // indicates whether this connection is in process of stopping
   209  	stopChan     chan struct{}                   // a method to stop the server-side gRPC call from a different go-routine
   210  	sync.RWMutex                                 // synchronizes access to shared variables
   211  }
   212  
   213  func (conn *connection) close() {
   214  	if conn.toDie() {
   215  		return
   216  	}
   217  
   218  	amIFirst := atomic.CompareAndSwapInt32(&conn.stopFlag, int32(0), int32(1))
   219  	if !amIFirst {
   220  		return
   221  	}
   222  
   223  	conn.stopChan <- struct{}{}
   224  
   225  	conn.Lock()
   226  
   227  	if conn.clientStream != nil {
   228  		conn.clientStream.CloseSend()
   229  	}
   230  	if conn.conn != nil {
   231  		conn.conn.Close()
   232  	}
   233  
   234  	conn.Unlock()
   235  
   236  }
   237  
   238  func (conn *connection) toDie() bool {
   239  	return atomic.LoadInt32(&(conn.stopFlag)) == int32(1)
   240  }
   241  
   242  func (conn *connection) send(msg *proto.SignedGossipMessage, onErr func(error)) {
   243  	conn.Lock()
   244  	defer conn.Unlock()
   245  
   246  	if len(conn.outBuff) == util.GetIntOrDefault("peer.gossip.sendBuffSize", defSendBuffSize) {
   247  		go onErr(errSendOverflow)
   248  		return
   249  	}
   250  
   251  	m := &msgSending{
   252  		envelope: msg.Envelope,
   253  		onErr:    onErr,
   254  	}
   255  
   256  	conn.outBuff <- m
   257  }
   258  
   259  func (conn *connection) serviceConnection() error {
   260  	errChan := make(chan error, 1)
   261  	msgChan := make(chan *proto.SignedGossipMessage, util.GetIntOrDefault("peer.gossip.recvBuffSize", defRecvBuffSize))
   262  	defer close(msgChan)
   263  
   264  	// Call stream.Recv() asynchronously in readFromStream(),
   265  	// and wait for either the Recv() call to end,
   266  	// or a signal to close the connection, which exits
   267  	// the method and makes the Recv() call to fail in the
   268  	// readFromStream() method
   269  	go conn.readFromStream(errChan, msgChan)
   270  
   271  	go conn.writeToStream()
   272  
   273  	for !conn.toDie() {
   274  		select {
   275  		case stop := <-conn.stopChan:
   276  			conn.logger.Debug("Closing reading from stream")
   277  			conn.stopChan <- stop
   278  			return nil
   279  		case err := <-errChan:
   280  			return err
   281  		case msg := <-msgChan:
   282  			conn.handler(msg)
   283  		}
   284  	}
   285  	return nil
   286  }
   287  
   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.envelope)
   298  			if err != nil {
   299  				go m.onErr(err)
   300  				return
   301  			}
   302  			break
   303  		case stop := <-conn.stopChan:
   304  			conn.logger.Debug("Closing writing to stream")
   305  			conn.stopChan <- stop
   306  			return
   307  		}
   308  	}
   309  }
   310  
   311  func (conn *connection) readFromStream(errChan chan error, msgChan chan *proto.SignedGossipMessage) {
   312  	defer func() {
   313  		recover()
   314  	}() // msgChan might be closed
   315  	for !conn.toDie() {
   316  		stream := conn.getStream()
   317  		if stream == nil {
   318  			conn.logger.Error(conn.pkiID, "Stream is nil, aborting!")
   319  			errChan <- errors.New("Stream is nil")
   320  			return
   321  		}
   322  		envelope, err := stream.Recv()
   323  		if conn.toDie() {
   324  			conn.logger.Debug(conn.pkiID, "canceling read because closing")
   325  			return
   326  		}
   327  		if err != nil {
   328  			errChan <- err
   329  			conn.logger.Debug(conn.pkiID, "Got error, aborting:", err)
   330  			return
   331  		}
   332  		msg, err := envelope.ToGossipMessage()
   333  		if err != nil {
   334  			errChan <- err
   335  			conn.logger.Warning(conn.pkiID, "Got error, aborting:", err)
   336  		}
   337  		msgChan <- msg
   338  	}
   339  }
   340  
   341  func (conn *connection) getStream() stream {
   342  	conn.Lock()
   343  	defer conn.Unlock()
   344  
   345  	if conn.clientStream != nil && conn.serverStream != nil {
   346  		e := "Both client and server stream are not nil, something went wrong"
   347  		conn.logger.Error(e)
   348  	}
   349  
   350  	if conn.clientStream != nil {
   351  		return conn.clientStream
   352  	}
   353  
   354  	if conn.serverStream != nil {
   355  		return conn.serverStream
   356  	}
   357  
   358  	return nil
   359  }
   360  
   361  type msgSending struct {
   362  	envelope *proto.Envelope
   363  	onErr    func(error)
   364  }