github.com/evdatsion/aphelion-dpos-bft@v0.32.1/abci/client/socket_client.go (about)

     1  package abcicli
     2  
     3  import (
     4  	"bufio"
     5  	"container/list"
     6  	"errors"
     7  	"fmt"
     8  	"net"
     9  	"reflect"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/evdatsion/aphelion-dpos-bft/abci/types"
    14  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    15  )
    16  
    17  const reqQueueSize = 256 // TODO make configurable
    18  // const maxResponseSize = 1048576 // 1MB TODO make configurable
    19  const flushThrottleMS = 20 // Don't wait longer than...
    20  
    21  var _ Client = (*socketClient)(nil)
    22  
    23  // This is goroutine-safe, but users should beware that
    24  // the application in general is not meant to be interfaced
    25  // with concurrent callers.
    26  type socketClient struct {
    27  	cmn.BaseService
    28  
    29  	addr        string
    30  	mustConnect bool
    31  	conn        net.Conn
    32  
    33  	reqQueue   chan *ReqRes
    34  	flushTimer *cmn.ThrottleTimer
    35  
    36  	mtx     sync.Mutex
    37  	err     error
    38  	reqSent *list.List                            // list of requests sent, waiting for response
    39  	resCb   func(*types.Request, *types.Response) // called on all requests, if set.
    40  
    41  }
    42  
    43  func NewSocketClient(addr string, mustConnect bool) *socketClient {
    44  	cli := &socketClient{
    45  		reqQueue:    make(chan *ReqRes, reqQueueSize),
    46  		flushTimer:  cmn.NewThrottleTimer("socketClient", flushThrottleMS),
    47  		mustConnect: mustConnect,
    48  
    49  		addr:    addr,
    50  		reqSent: list.New(),
    51  		resCb:   nil,
    52  	}
    53  	cli.BaseService = *cmn.NewBaseService(nil, "socketClient", cli)
    54  	return cli
    55  }
    56  
    57  func (cli *socketClient) OnStart() error {
    58  	var err error
    59  	var conn net.Conn
    60  RETRY_LOOP:
    61  	for {
    62  		conn, err = cmn.Connect(cli.addr)
    63  		if err != nil {
    64  			if cli.mustConnect {
    65  				return err
    66  			}
    67  			cli.Logger.Error(fmt.Sprintf("abci.socketClient failed to connect to %v.  Retrying...", cli.addr), "err", err)
    68  			time.Sleep(time.Second * dialRetryIntervalSeconds)
    69  			continue RETRY_LOOP
    70  		}
    71  		cli.conn = conn
    72  
    73  		go cli.sendRequestsRoutine(conn)
    74  		go cli.recvResponseRoutine(conn)
    75  
    76  		return nil
    77  	}
    78  }
    79  
    80  func (cli *socketClient) OnStop() {
    81  	if cli.conn != nil {
    82  		cli.conn.Close()
    83  	}
    84  
    85  	cli.mtx.Lock()
    86  	defer cli.mtx.Unlock()
    87  	cli.flushQueue()
    88  }
    89  
    90  // Stop the client and set the error
    91  func (cli *socketClient) StopForError(err error) {
    92  	if !cli.IsRunning() {
    93  		return
    94  	}
    95  
    96  	cli.mtx.Lock()
    97  	if cli.err == nil {
    98  		cli.err = err
    99  	}
   100  	cli.mtx.Unlock()
   101  
   102  	cli.Logger.Error(fmt.Sprintf("Stopping abci.socketClient for error: %v", err.Error()))
   103  	cli.Stop()
   104  }
   105  
   106  func (cli *socketClient) Error() error {
   107  	cli.mtx.Lock()
   108  	defer cli.mtx.Unlock()
   109  	return cli.err
   110  }
   111  
   112  // Set listener for all responses
   113  // NOTE: callback may get internally generated flush responses.
   114  func (cli *socketClient) SetResponseCallback(resCb Callback) {
   115  	cli.mtx.Lock()
   116  	cli.resCb = resCb
   117  	cli.mtx.Unlock()
   118  }
   119  
   120  //----------------------------------------
   121  
   122  func (cli *socketClient) sendRequestsRoutine(conn net.Conn) {
   123  
   124  	w := bufio.NewWriter(conn)
   125  	for {
   126  		select {
   127  		case <-cli.flushTimer.Ch:
   128  			select {
   129  			case cli.reqQueue <- NewReqRes(types.ToRequestFlush()):
   130  			default:
   131  				// Probably will fill the buffer, or retry later.
   132  			}
   133  		case <-cli.Quit():
   134  			return
   135  		case reqres := <-cli.reqQueue:
   136  			cli.willSendReq(reqres)
   137  			err := types.WriteMessage(reqres.Request, w)
   138  			if err != nil {
   139  				cli.StopForError(fmt.Errorf("Error writing msg: %v", err))
   140  				return
   141  			}
   142  			// cli.Logger.Debug("Sent request", "requestType", reflect.TypeOf(reqres.Request), "request", reqres.Request)
   143  			if _, ok := reqres.Request.Value.(*types.Request_Flush); ok {
   144  				err = w.Flush()
   145  				if err != nil {
   146  					cli.StopForError(fmt.Errorf("Error flushing writer: %v", err))
   147  					return
   148  				}
   149  			}
   150  		}
   151  	}
   152  }
   153  
   154  func (cli *socketClient) recvResponseRoutine(conn net.Conn) {
   155  
   156  	r := bufio.NewReader(conn) // Buffer reads
   157  	for {
   158  		var res = &types.Response{}
   159  		err := types.ReadMessage(r, res)
   160  		if err != nil {
   161  			cli.StopForError(err)
   162  			return
   163  		}
   164  		switch r := res.Value.(type) {
   165  		case *types.Response_Exception:
   166  			// XXX After setting cli.err, release waiters (e.g. reqres.Done())
   167  			cli.StopForError(errors.New(r.Exception.Error))
   168  			return
   169  		default:
   170  			// cli.Logger.Debug("Received response", "responseType", reflect.TypeOf(res), "response", res)
   171  			err := cli.didRecvResponse(res)
   172  			if err != nil {
   173  				cli.StopForError(err)
   174  				return
   175  			}
   176  		}
   177  	}
   178  }
   179  
   180  func (cli *socketClient) willSendReq(reqres *ReqRes) {
   181  	cli.mtx.Lock()
   182  	defer cli.mtx.Unlock()
   183  	cli.reqSent.PushBack(reqres)
   184  }
   185  
   186  func (cli *socketClient) didRecvResponse(res *types.Response) error {
   187  	cli.mtx.Lock()
   188  	defer cli.mtx.Unlock()
   189  
   190  	// Get the first ReqRes
   191  	next := cli.reqSent.Front()
   192  	if next == nil {
   193  		return fmt.Errorf("Unexpected result type %v when nothing expected", reflect.TypeOf(res.Value))
   194  	}
   195  	reqres := next.Value.(*ReqRes)
   196  	if !resMatchesReq(reqres.Request, res) {
   197  		return fmt.Errorf("Unexpected result type %v when response to %v expected",
   198  			reflect.TypeOf(res.Value), reflect.TypeOf(reqres.Request.Value))
   199  	}
   200  
   201  	reqres.Response = res    // Set response
   202  	reqres.Done()            // Release waiters
   203  	cli.reqSent.Remove(next) // Pop first item from linked list
   204  
   205  	// Notify client listener if set (global callback).
   206  	if cli.resCb != nil {
   207  		cli.resCb(reqres.Request, res)
   208  	}
   209  
   210  	// Notify reqRes listener if set (request specific callback).
   211  	// NOTE: it is possible this callback isn't set on the reqres object.
   212  	// at this point, in which case it will be called after, when it is set.
   213  	if cb := reqres.GetCallback(); cb != nil {
   214  		cb(res)
   215  	}
   216  
   217  	return nil
   218  }
   219  
   220  //----------------------------------------
   221  
   222  func (cli *socketClient) EchoAsync(msg string) *ReqRes {
   223  	return cli.queueRequest(types.ToRequestEcho(msg))
   224  }
   225  
   226  func (cli *socketClient) FlushAsync() *ReqRes {
   227  	return cli.queueRequest(types.ToRequestFlush())
   228  }
   229  
   230  func (cli *socketClient) InfoAsync(req types.RequestInfo) *ReqRes {
   231  	return cli.queueRequest(types.ToRequestInfo(req))
   232  }
   233  
   234  func (cli *socketClient) SetOptionAsync(req types.RequestSetOption) *ReqRes {
   235  	return cli.queueRequest(types.ToRequestSetOption(req))
   236  }
   237  
   238  func (cli *socketClient) DeliverTxAsync(req types.RequestDeliverTx) *ReqRes {
   239  	return cli.queueRequest(types.ToRequestDeliverTx(req))
   240  }
   241  
   242  func (cli *socketClient) CheckTxAsync(req types.RequestCheckTx) *ReqRes {
   243  	return cli.queueRequest(types.ToRequestCheckTx(req))
   244  }
   245  
   246  func (cli *socketClient) QueryAsync(req types.RequestQuery) *ReqRes {
   247  	return cli.queueRequest(types.ToRequestQuery(req))
   248  }
   249  
   250  func (cli *socketClient) CommitAsync() *ReqRes {
   251  	return cli.queueRequest(types.ToRequestCommit())
   252  }
   253  
   254  func (cli *socketClient) InitChainAsync(req types.RequestInitChain) *ReqRes {
   255  	return cli.queueRequest(types.ToRequestInitChain(req))
   256  }
   257  
   258  func (cli *socketClient) BeginBlockAsync(req types.RequestBeginBlock) *ReqRes {
   259  	return cli.queueRequest(types.ToRequestBeginBlock(req))
   260  }
   261  
   262  func (cli *socketClient) EndBlockAsync(req types.RequestEndBlock) *ReqRes {
   263  	return cli.queueRequest(types.ToRequestEndBlock(req))
   264  }
   265  
   266  //----------------------------------------
   267  
   268  func (cli *socketClient) FlushSync() error {
   269  	reqRes := cli.queueRequest(types.ToRequestFlush())
   270  	if err := cli.Error(); err != nil {
   271  		return err
   272  	}
   273  	reqRes.Wait() // NOTE: if we don't flush the queue, its possible to get stuck here
   274  	return cli.Error()
   275  }
   276  
   277  func (cli *socketClient) EchoSync(msg string) (*types.ResponseEcho, error) {
   278  	reqres := cli.queueRequest(types.ToRequestEcho(msg))
   279  	cli.FlushSync()
   280  	return reqres.Response.GetEcho(), cli.Error()
   281  }
   282  
   283  func (cli *socketClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) {
   284  	reqres := cli.queueRequest(types.ToRequestInfo(req))
   285  	cli.FlushSync()
   286  	return reqres.Response.GetInfo(), cli.Error()
   287  }
   288  
   289  func (cli *socketClient) SetOptionSync(req types.RequestSetOption) (*types.ResponseSetOption, error) {
   290  	reqres := cli.queueRequest(types.ToRequestSetOption(req))
   291  	cli.FlushSync()
   292  	return reqres.Response.GetSetOption(), cli.Error()
   293  }
   294  
   295  func (cli *socketClient) DeliverTxSync(req types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
   296  	reqres := cli.queueRequest(types.ToRequestDeliverTx(req))
   297  	cli.FlushSync()
   298  	return reqres.Response.GetDeliverTx(), cli.Error()
   299  }
   300  
   301  func (cli *socketClient) CheckTxSync(req types.RequestCheckTx) (*types.ResponseCheckTx, error) {
   302  	reqres := cli.queueRequest(types.ToRequestCheckTx(req))
   303  	cli.FlushSync()
   304  	return reqres.Response.GetCheckTx(), cli.Error()
   305  }
   306  
   307  func (cli *socketClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) {
   308  	reqres := cli.queueRequest(types.ToRequestQuery(req))
   309  	cli.FlushSync()
   310  	return reqres.Response.GetQuery(), cli.Error()
   311  }
   312  
   313  func (cli *socketClient) CommitSync() (*types.ResponseCommit, error) {
   314  	reqres := cli.queueRequest(types.ToRequestCommit())
   315  	cli.FlushSync()
   316  	return reqres.Response.GetCommit(), cli.Error()
   317  }
   318  
   319  func (cli *socketClient) InitChainSync(req types.RequestInitChain) (*types.ResponseInitChain, error) {
   320  	reqres := cli.queueRequest(types.ToRequestInitChain(req))
   321  	cli.FlushSync()
   322  	return reqres.Response.GetInitChain(), cli.Error()
   323  }
   324  
   325  func (cli *socketClient) BeginBlockSync(req types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
   326  	reqres := cli.queueRequest(types.ToRequestBeginBlock(req))
   327  	cli.FlushSync()
   328  	return reqres.Response.GetBeginBlock(), cli.Error()
   329  }
   330  
   331  func (cli *socketClient) EndBlockSync(req types.RequestEndBlock) (*types.ResponseEndBlock, error) {
   332  	reqres := cli.queueRequest(types.ToRequestEndBlock(req))
   333  	cli.FlushSync()
   334  	return reqres.Response.GetEndBlock(), cli.Error()
   335  }
   336  
   337  //----------------------------------------
   338  
   339  func (cli *socketClient) queueRequest(req *types.Request) *ReqRes {
   340  	reqres := NewReqRes(req)
   341  
   342  	// TODO: set cli.err if reqQueue times out
   343  	cli.reqQueue <- reqres
   344  
   345  	// Maybe auto-flush, or unset auto-flush
   346  	switch req.Value.(type) {
   347  	case *types.Request_Flush:
   348  		cli.flushTimer.Unset()
   349  	default:
   350  		cli.flushTimer.Set()
   351  	}
   352  
   353  	return reqres
   354  }
   355  
   356  func (cli *socketClient) flushQueue() {
   357  	// mark all in-flight messages as resolved (they will get cli.Error())
   358  	for req := cli.reqSent.Front(); req != nil; req = req.Next() {
   359  		reqres := req.Value.(*ReqRes)
   360  		reqres.Done()
   361  	}
   362  
   363  	// mark all queued messages as resolved
   364  LOOP:
   365  	for {
   366  		select {
   367  		case reqres := <-cli.reqQueue:
   368  			reqres.Done()
   369  		default:
   370  			break LOOP
   371  		}
   372  	}
   373  }
   374  
   375  //----------------------------------------
   376  
   377  func resMatchesReq(req *types.Request, res *types.Response) (ok bool) {
   378  	switch req.Value.(type) {
   379  	case *types.Request_Echo:
   380  		_, ok = res.Value.(*types.Response_Echo)
   381  	case *types.Request_Flush:
   382  		_, ok = res.Value.(*types.Response_Flush)
   383  	case *types.Request_Info:
   384  		_, ok = res.Value.(*types.Response_Info)
   385  	case *types.Request_SetOption:
   386  		_, ok = res.Value.(*types.Response_SetOption)
   387  	case *types.Request_DeliverTx:
   388  		_, ok = res.Value.(*types.Response_DeliverTx)
   389  	case *types.Request_CheckTx:
   390  		_, ok = res.Value.(*types.Response_CheckTx)
   391  	case *types.Request_Commit:
   392  		_, ok = res.Value.(*types.Response_Commit)
   393  	case *types.Request_Query:
   394  		_, ok = res.Value.(*types.Response_Query)
   395  	case *types.Request_InitChain:
   396  		_, ok = res.Value.(*types.Response_InitChain)
   397  	case *types.Request_BeginBlock:
   398  		_, ok = res.Value.(*types.Response_BeginBlock)
   399  	case *types.Request_EndBlock:
   400  		_, ok = res.Value.(*types.Response_EndBlock)
   401  	}
   402  	return ok
   403  }