github.com/number571/tendermint@v0.34.11-gost/abci/client/socket_client.go (about)

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