github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/abci/client/socket_client.go (about)

     1  package abcicli
     2  
     3  import (
     4  	"bufio"
     5  	"container/list"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net"
    10  	"reflect"
    11  	"time"
    12  
    13  	"github.com/tendermint/tendermint/abci/types"
    14  
    15  	ocabci "github.com/line/ostracon/abci/types"
    16  	tmnet "github.com/line/ostracon/libs/net"
    17  	"github.com/line/ostracon/libs/service"
    18  	tmsync "github.com/line/ostracon/libs/sync"
    19  	"github.com/line/ostracon/libs/timer"
    20  )
    21  
    22  const (
    23  	reqQueueSize    = 256 // TODO make configurable
    24  	flushThrottleMS = 20  // Don't wait longer than...
    25  )
    26  
    27  // This is goroutine-safe, but users should beware that the application in
    28  // general is not meant to be interfaced with concurrent callers.
    29  type socketClient struct {
    30  	service.BaseService
    31  
    32  	addr        string
    33  	mustConnect bool
    34  	conn        net.Conn
    35  
    36  	reqQueue   chan *ReqRes
    37  	flushTimer *timer.ThrottleTimer
    38  
    39  	mtx     tmsync.Mutex
    40  	err     error
    41  	reqSent *list.List // list of requests sent, waiting for response
    42  
    43  	globalCbMtx tmsync.Mutex
    44  	globalCb    GlobalCallback
    45  }
    46  
    47  var _ Client = (*socketClient)(nil)
    48  
    49  // NewSocketClient creates a new socket client, which connects to a given
    50  // address. If mustConnect is true, the client will return an error upon start
    51  // if it fails to connect.
    52  func NewSocketClient(addr string, mustConnect bool) Client {
    53  	cli := &socketClient{
    54  		reqQueue:    make(chan *ReqRes, reqQueueSize),
    55  		flushTimer:  timer.NewThrottleTimer("socketClient", flushThrottleMS),
    56  		mustConnect: mustConnect,
    57  
    58  		addr:     addr,
    59  		reqSent:  list.New(),
    60  		globalCb: nil,
    61  	}
    62  	cli.BaseService = *service.NewBaseService(nil, "socketClient", cli)
    63  	return cli
    64  }
    65  
    66  // OnStart implements Service by connecting to the server and spawning reading
    67  // and writing goroutines.
    68  func (cli *socketClient) OnStart() error {
    69  	var (
    70  		err  error
    71  		conn net.Conn
    72  	)
    73  
    74  	for {
    75  		conn, err = tmnet.Connect(cli.addr)
    76  		if err != nil {
    77  			if cli.mustConnect {
    78  				return err
    79  			}
    80  			cli.Logger.Error(fmt.Sprintf("abci.socketClient failed to connect to %v.  Retrying after %vs...",
    81  				cli.addr, dialRetryIntervalSeconds), "err", err)
    82  			time.Sleep(time.Second * dialRetryIntervalSeconds)
    83  			continue
    84  		}
    85  		cli.conn = conn
    86  
    87  		go cli.sendRequestsRoutine(conn)
    88  		go cli.recvResponseRoutine(conn)
    89  
    90  		return nil
    91  	}
    92  }
    93  
    94  // OnStop implements Service by closing connection and flushing all queues.
    95  func (cli *socketClient) OnStop() {
    96  	if cli.conn != nil {
    97  		cli.conn.Close()
    98  	}
    99  
   100  	cli.flushQueue()
   101  	cli.flushTimer.Stop()
   102  }
   103  
   104  // Error returns an error if the client was stopped abruptly.
   105  func (cli *socketClient) Error() error {
   106  	cli.mtx.Lock()
   107  	defer cli.mtx.Unlock()
   108  	return cli.err
   109  }
   110  
   111  func (cli *socketClient) SetGlobalCallback(globalCb GlobalCallback) {
   112  	cli.globalCbMtx.Lock()
   113  	defer cli.globalCbMtx.Unlock()
   114  	cli.globalCb = globalCb
   115  }
   116  
   117  func (cli *socketClient) GetGlobalCallback() (cb GlobalCallback) {
   118  	cli.globalCbMtx.Lock()
   119  	defer cli.globalCbMtx.Unlock()
   120  	cb = cli.globalCb
   121  	return cb
   122  }
   123  
   124  //----------------------------------------
   125  
   126  func (cli *socketClient) sendRequestsRoutine(conn io.Writer) {
   127  	w := bufio.NewWriter(conn)
   128  	for {
   129  		select {
   130  		case reqres := <-cli.reqQueue:
   131  			// cli.Logger.Debug("Sent request", "requestType", reflect.TypeOf(reqres.Request), "request", reqres.Request)
   132  
   133  			cli.willSendReq(reqres)
   134  			err := ocabci.WriteMessage(reqres.Request, w)
   135  			if err != nil {
   136  				cli.stopForError(fmt.Errorf("write to buffer: %w", err))
   137  				return
   138  			}
   139  
   140  			// If it's a flush request, flush the current buffer.
   141  			if _, ok := reqres.Request.Value.(*ocabci.Request_Flush); ok {
   142  				err = w.Flush()
   143  				if err != nil {
   144  					cli.stopForError(fmt.Errorf("flush buffer: %w", err))
   145  					return
   146  				}
   147  			}
   148  		case <-cli.flushTimer.Ch: // flush queue
   149  			select {
   150  			case cli.reqQueue <- NewReqRes(ocabci.ToRequestFlush(), nil):
   151  			default:
   152  				// Probably will fill the buffer, or retry later.
   153  			}
   154  		case <-cli.Quit():
   155  			return
   156  		}
   157  	}
   158  }
   159  
   160  func (cli *socketClient) recvResponseRoutine(conn io.Reader) {
   161  	r := bufio.NewReader(conn)
   162  	for {
   163  		var res = &ocabci.Response{}
   164  		err := ocabci.ReadMessage(r, res)
   165  		if err != nil {
   166  			cli.stopForError(fmt.Errorf("read message: %w", err))
   167  			return
   168  		}
   169  
   170  		// cli.Logger.Debug("Received response", "responseType", reflect.TypeOf(res), "response", res)
   171  
   172  		switch r := res.Value.(type) {
   173  		case *ocabci.Response_Exception: // app responded with error
   174  			// XXX After setting cli.err, release waiters (e.g. reqres.Done())
   175  			cli.stopForError(errors.New(r.Exception.Error))
   176  			return
   177  		default:
   178  			err := cli.didRecvResponse(res)
   179  			if err != nil {
   180  				cli.stopForError(err)
   181  				return
   182  			}
   183  		}
   184  	}
   185  }
   186  
   187  func (cli *socketClient) willSendReq(reqres *ReqRes) {
   188  	cli.mtx.Lock()
   189  	defer cli.mtx.Unlock()
   190  	cli.reqSent.PushBack(reqres)
   191  }
   192  
   193  func (cli *socketClient) didRecvResponse(res *ocabci.Response) error {
   194  	cli.mtx.Lock()
   195  	defer cli.mtx.Unlock()
   196  
   197  	// Get the first ReqRes.
   198  	next := cli.reqSent.Front()
   199  	if next == nil {
   200  		return fmt.Errorf("unexpected %v when nothing expected", reflect.TypeOf(res.Value))
   201  	}
   202  
   203  	reqres := next.Value.(*ReqRes)
   204  	if !resMatchesReq(reqres.Request, res) {
   205  		return fmt.Errorf("unexpected %v when response to %v expected",
   206  			reflect.TypeOf(res.Value), reflect.TypeOf(reqres.Request.Value))
   207  	}
   208  
   209  	reqres.Response = res
   210  	reqres.wg.Done()         // release waiters
   211  	cli.reqSent.Remove(next) // pop first item from linked list
   212  
   213  	// Notify client listener if set (global callback).
   214  	if cli.globalCb != nil {
   215  		cli.globalCb(reqres.Request, res)
   216  	}
   217  
   218  	// Notify reqRes listener if set (request specific callback).
   219  	//
   220  	// NOTE: It is possible this callback isn't set on the reqres object. At this
   221  	// point, in which case it will be called after, when it is set.
   222  	reqres.InvokeCallback()
   223  
   224  	return nil
   225  }
   226  
   227  //----------------------------------------
   228  
   229  func (cli *socketClient) EchoAsync(msg string, cb ResponseCallback) *ReqRes {
   230  	return cli.queueRequest(ocabci.ToRequestEcho(msg), cb)
   231  }
   232  
   233  func (cli *socketClient) FlushAsync(cb ResponseCallback) *ReqRes {
   234  	return cli.queueRequest(ocabci.ToRequestFlush(), cb)
   235  }
   236  
   237  func (cli *socketClient) InfoAsync(req types.RequestInfo, cb ResponseCallback) *ReqRes {
   238  	return cli.queueRequest(ocabci.ToRequestInfo(req), cb)
   239  }
   240  
   241  func (cli *socketClient) SetOptionAsync(req types.RequestSetOption, cb ResponseCallback) *ReqRes {
   242  	return cli.queueRequest(ocabci.ToRequestSetOption(req), cb)
   243  }
   244  
   245  func (cli *socketClient) DeliverTxAsync(req types.RequestDeliverTx, cb ResponseCallback) *ReqRes {
   246  	return cli.queueRequest(ocabci.ToRequestDeliverTx(req), cb)
   247  }
   248  
   249  func (cli *socketClient) CheckTxAsync(req types.RequestCheckTx, cb ResponseCallback) *ReqRes {
   250  	return cli.queueRequest(ocabci.ToRequestCheckTx(req), cb)
   251  }
   252  
   253  func (cli *socketClient) QueryAsync(req types.RequestQuery, cb ResponseCallback) *ReqRes {
   254  	return cli.queueRequest(ocabci.ToRequestQuery(req), cb)
   255  }
   256  
   257  func (cli *socketClient) CommitAsync(cb ResponseCallback) *ReqRes {
   258  	return cli.queueRequest(ocabci.ToRequestCommit(), cb)
   259  }
   260  
   261  func (cli *socketClient) InitChainAsync(req types.RequestInitChain, cb ResponseCallback) *ReqRes {
   262  	return cli.queueRequest(ocabci.ToRequestInitChain(req), cb)
   263  }
   264  
   265  func (cli *socketClient) BeginBlockAsync(req ocabci.RequestBeginBlock, cb ResponseCallback) *ReqRes {
   266  	return cli.queueRequest(ocabci.ToRequestBeginBlock(req), cb)
   267  }
   268  
   269  func (cli *socketClient) EndBlockAsync(req types.RequestEndBlock, cb ResponseCallback) *ReqRes {
   270  	return cli.queueRequest(ocabci.ToRequestEndBlock(req), cb)
   271  }
   272  
   273  func (cli *socketClient) BeginRecheckTxAsync(req ocabci.RequestBeginRecheckTx, cb ResponseCallback) *ReqRes {
   274  	return cli.queueRequest(ocabci.ToRequestBeginRecheckTx(req), cb)
   275  }
   276  
   277  func (cli *socketClient) EndRecheckTxAsync(req ocabci.RequestEndRecheckTx, cb ResponseCallback) *ReqRes {
   278  	return cli.queueRequest(ocabci.ToRequestEndRecheckTx(req), cb)
   279  }
   280  
   281  func (cli *socketClient) ListSnapshotsAsync(req types.RequestListSnapshots, cb ResponseCallback) *ReqRes {
   282  	return cli.queueRequest(ocabci.ToRequestListSnapshots(req), cb)
   283  }
   284  
   285  func (cli *socketClient) OfferSnapshotAsync(req types.RequestOfferSnapshot, cb ResponseCallback) *ReqRes {
   286  	return cli.queueRequest(ocabci.ToRequestOfferSnapshot(req), cb)
   287  }
   288  
   289  func (cli *socketClient) LoadSnapshotChunkAsync(req types.RequestLoadSnapshotChunk, cb ResponseCallback) *ReqRes {
   290  	return cli.queueRequest(ocabci.ToRequestLoadSnapshotChunk(req), cb)
   291  }
   292  
   293  func (cli *socketClient) ApplySnapshotChunkAsync(req types.RequestApplySnapshotChunk, cb ResponseCallback) *ReqRes {
   294  	return cli.queueRequest(ocabci.ToRequestApplySnapshotChunk(req), cb)
   295  }
   296  
   297  //----------------------------------------
   298  
   299  func (cli *socketClient) FlushSync() (*types.ResponseFlush, error) {
   300  	reqRes := cli.queueRequest(ocabci.ToRequestFlush(), nil)
   301  	if err := cli.Error(); err != nil {
   302  		return nil, err
   303  	}
   304  	reqRes.Wait() // NOTE: if we don't flush the queue, its possible to get stuck here
   305  	return reqRes.Response.GetFlush(), cli.Error()
   306  }
   307  
   308  func (cli *socketClient) EchoSync(msg string) (*types.ResponseEcho, error) {
   309  	reqres := cli.queueRequest(ocabci.ToRequestEcho(msg), nil)
   310  	if _, err := cli.FlushSync(); err != nil {
   311  		return nil, err
   312  	}
   313  
   314  	return reqres.Response.GetEcho(), cli.Error()
   315  }
   316  
   317  func (cli *socketClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) {
   318  	reqres := cli.queueRequest(ocabci.ToRequestInfo(req), nil)
   319  	if _, err := cli.FlushSync(); err != nil {
   320  		return nil, err
   321  	}
   322  
   323  	return reqres.Response.GetInfo(), cli.Error()
   324  }
   325  
   326  func (cli *socketClient) SetOptionSync(req types.RequestSetOption) (*types.ResponseSetOption, error) {
   327  	reqres := cli.queueRequest(ocabci.ToRequestSetOption(req), nil)
   328  	if _, err := cli.FlushSync(); err != nil {
   329  		return nil, err
   330  	}
   331  
   332  	return reqres.Response.GetSetOption(), cli.Error()
   333  }
   334  
   335  func (cli *socketClient) DeliverTxSync(req types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
   336  	reqres := cli.queueRequest(ocabci.ToRequestDeliverTx(req), nil)
   337  	if _, err := cli.FlushSync(); err != nil {
   338  		return nil, err
   339  	}
   340  
   341  	return reqres.Response.GetDeliverTx(), cli.Error()
   342  }
   343  
   344  func (cli *socketClient) CheckTxSync(req types.RequestCheckTx) (*ocabci.ResponseCheckTx, error) {
   345  	reqres := cli.queueRequest(ocabci.ToRequestCheckTx(req), nil)
   346  	if _, err := cli.FlushSync(); err != nil {
   347  		return nil, err
   348  	}
   349  
   350  	return reqres.Response.GetCheckTx(), cli.Error()
   351  }
   352  
   353  func (cli *socketClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) {
   354  	reqres := cli.queueRequest(ocabci.ToRequestQuery(req), nil)
   355  	if _, err := cli.FlushSync(); err != nil {
   356  		return nil, err
   357  	}
   358  
   359  	return reqres.Response.GetQuery(), cli.Error()
   360  }
   361  
   362  func (cli *socketClient) CommitSync() (*types.ResponseCommit, error) {
   363  	reqres := cli.queueRequest(ocabci.ToRequestCommit(), nil)
   364  	if _, err := cli.FlushSync(); err != nil {
   365  		return nil, err
   366  	}
   367  
   368  	return reqres.Response.GetCommit(), cli.Error()
   369  }
   370  
   371  func (cli *socketClient) InitChainSync(req types.RequestInitChain) (*types.ResponseInitChain, error) {
   372  	reqres := cli.queueRequest(ocabci.ToRequestInitChain(req), nil)
   373  	if _, err := cli.FlushSync(); err != nil {
   374  		return nil, err
   375  	}
   376  
   377  	return reqres.Response.GetInitChain(), cli.Error()
   378  }
   379  
   380  func (cli *socketClient) BeginBlockSync(req ocabci.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
   381  	reqres := cli.queueRequest(ocabci.ToRequestBeginBlock(req), nil)
   382  	if _, err := cli.FlushSync(); err != nil {
   383  		return nil, err
   384  	}
   385  
   386  	return reqres.Response.GetBeginBlock(), cli.Error()
   387  }
   388  
   389  func (cli *socketClient) EndBlockSync(req types.RequestEndBlock) (*types.ResponseEndBlock, error) {
   390  	reqres := cli.queueRequest(ocabci.ToRequestEndBlock(req), nil)
   391  	if _, err := cli.FlushSync(); err != nil {
   392  		return nil, err
   393  	}
   394  
   395  	return reqres.Response.GetEndBlock(), cli.Error()
   396  }
   397  
   398  func (cli *socketClient) BeginRecheckTxSync(req ocabci.RequestBeginRecheckTx) (*ocabci.ResponseBeginRecheckTx, error) {
   399  	reqres := cli.queueRequest(ocabci.ToRequestBeginRecheckTx(req), nil)
   400  	if _, err := cli.FlushSync(); err != nil {
   401  		return nil, err
   402  	}
   403  
   404  	return reqres.Response.GetBeginRecheckTx(), cli.Error()
   405  }
   406  
   407  func (cli *socketClient) EndRecheckTxSync(req ocabci.RequestEndRecheckTx) (*ocabci.ResponseEndRecheckTx, error) {
   408  	reqres := cli.queueRequest(ocabci.ToRequestEndRecheckTx(req), nil)
   409  	if _, err := cli.FlushSync(); err != nil {
   410  		return nil, err
   411  	}
   412  
   413  	return reqres.Response.GetEndRecheckTx(), cli.Error()
   414  }
   415  
   416  func (cli *socketClient) ListSnapshotsSync(req types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
   417  	reqres := cli.queueRequest(ocabci.ToRequestListSnapshots(req), nil)
   418  	if _, err := cli.FlushSync(); err != nil {
   419  		return nil, err
   420  	}
   421  
   422  	return reqres.Response.GetListSnapshots(), cli.Error()
   423  }
   424  
   425  func (cli *socketClient) OfferSnapshotSync(req types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
   426  	reqres := cli.queueRequest(ocabci.ToRequestOfferSnapshot(req), nil)
   427  	if _, err := cli.FlushSync(); err != nil {
   428  		return nil, err
   429  	}
   430  
   431  	return reqres.Response.GetOfferSnapshot(), cli.Error()
   432  }
   433  
   434  func (cli *socketClient) LoadSnapshotChunkSync(
   435  	req types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
   436  	reqres := cli.queueRequest(ocabci.ToRequestLoadSnapshotChunk(req), nil)
   437  	if _, err := cli.FlushSync(); err != nil {
   438  		return nil, err
   439  	}
   440  
   441  	return reqres.Response.GetLoadSnapshotChunk(), cli.Error()
   442  }
   443  
   444  func (cli *socketClient) ApplySnapshotChunkSync(
   445  	req types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
   446  	reqres := cli.queueRequest(ocabci.ToRequestApplySnapshotChunk(req), nil)
   447  	if _, err := cli.FlushSync(); err != nil {
   448  		return nil, err
   449  	}
   450  	return reqres.Response.GetApplySnapshotChunk(), cli.Error()
   451  }
   452  
   453  //----------------------------------------
   454  
   455  func (cli *socketClient) queueRequest(req *ocabci.Request, cb ResponseCallback) *ReqRes {
   456  	reqres := NewReqRes(req, cb)
   457  
   458  	// TODO: set cli.err if reqQueue times out
   459  	cli.reqQueue <- reqres
   460  
   461  	// Maybe auto-flush, or unset auto-flush
   462  	switch req.Value.(type) {
   463  	case *ocabci.Request_Flush:
   464  		cli.flushTimer.Unset()
   465  	default:
   466  		cli.flushTimer.Set()
   467  	}
   468  
   469  	return reqres
   470  }
   471  
   472  func (cli *socketClient) flushQueue() {
   473  	cli.mtx.Lock()
   474  	defer cli.mtx.Unlock()
   475  
   476  	// mark all in-flight messages as resolved (they will get cli.Error())
   477  	for req := cli.reqSent.Front(); req != nil; req = req.Next() {
   478  		reqres := req.Value.(*ReqRes)
   479  		reqres.wg.Done()
   480  	}
   481  
   482  	// mark all queued messages as resolved
   483  LOOP:
   484  	for {
   485  		select {
   486  		case reqres := <-cli.reqQueue:
   487  			reqres.wg.Done()
   488  		default:
   489  			break LOOP
   490  		}
   491  	}
   492  }
   493  
   494  //----------------------------------------
   495  
   496  func resMatchesReq(req *ocabci.Request, res *ocabci.Response) (ok bool) {
   497  	switch req.Value.(type) {
   498  	case *ocabci.Request_Echo:
   499  		_, ok = res.Value.(*ocabci.Response_Echo)
   500  	case *ocabci.Request_Flush:
   501  		_, ok = res.Value.(*ocabci.Response_Flush)
   502  	case *ocabci.Request_Info:
   503  		_, ok = res.Value.(*ocabci.Response_Info)
   504  	case *ocabci.Request_SetOption:
   505  		_, ok = res.Value.(*ocabci.Response_SetOption)
   506  	case *ocabci.Request_DeliverTx:
   507  		_, ok = res.Value.(*ocabci.Response_DeliverTx)
   508  	case *ocabci.Request_CheckTx:
   509  		_, ok = res.Value.(*ocabci.Response_CheckTx)
   510  	case *ocabci.Request_Commit:
   511  		_, ok = res.Value.(*ocabci.Response_Commit)
   512  	case *ocabci.Request_Query:
   513  		_, ok = res.Value.(*ocabci.Response_Query)
   514  	case *ocabci.Request_InitChain:
   515  		_, ok = res.Value.(*ocabci.Response_InitChain)
   516  	case *ocabci.Request_BeginBlock:
   517  		_, ok = res.Value.(*ocabci.Response_BeginBlock)
   518  	case *ocabci.Request_EndBlock:
   519  		_, ok = res.Value.(*ocabci.Response_EndBlock)
   520  	case *ocabci.Request_BeginRecheckTx:
   521  		_, ok = res.Value.(*ocabci.Response_BeginRecheckTx)
   522  	case *ocabci.Request_EndRecheckTx:
   523  		_, ok = res.Value.(*ocabci.Response_EndRecheckTx)
   524  	case *ocabci.Request_ApplySnapshotChunk:
   525  		_, ok = res.Value.(*ocabci.Response_ApplySnapshotChunk)
   526  	case *ocabci.Request_LoadSnapshotChunk:
   527  		_, ok = res.Value.(*ocabci.Response_LoadSnapshotChunk)
   528  	case *ocabci.Request_ListSnapshots:
   529  		_, ok = res.Value.(*ocabci.Response_ListSnapshots)
   530  	case *ocabci.Request_OfferSnapshot:
   531  		_, ok = res.Value.(*ocabci.Response_OfferSnapshot)
   532  	}
   533  	return ok
   534  }
   535  
   536  func (cli *socketClient) stopForError(err error) {
   537  	if !cli.IsRunning() {
   538  		return
   539  	}
   540  
   541  	cli.mtx.Lock()
   542  	if cli.err == nil {
   543  		cli.err = err
   544  	}
   545  	cli.mtx.Unlock()
   546  
   547  	cli.Logger.Error(fmt.Sprintf("Stopping abci.socketClient for error: %v", err.Error()))
   548  	if err := cli.Stop(); err != nil {
   549  		cli.Logger.Error("Error stopping abci.socketClient", "err", err)
   550  	}
   551  }