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

     1  package abcicli
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"sync"
     7  	"time"
     8  
     9  	context "golang.org/x/net/context"
    10  	grpc "google.golang.org/grpc"
    11  
    12  	"github.com/evdatsion/aphelion-dpos-bft/abci/types"
    13  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    14  )
    15  
    16  var _ Client = (*grpcClient)(nil)
    17  
    18  // A stripped copy of the remoteClient that makes
    19  // synchronous calls using grpc
    20  type grpcClient struct {
    21  	cmn.BaseService
    22  	mustConnect bool
    23  
    24  	client types.ABCIApplicationClient
    25  	conn   *grpc.ClientConn
    26  
    27  	mtx   sync.Mutex
    28  	addr  string
    29  	err   error
    30  	resCb func(*types.Request, *types.Response) // listens to all callbacks
    31  }
    32  
    33  func NewGRPCClient(addr string, mustConnect bool) *grpcClient {
    34  	cli := &grpcClient{
    35  		addr:        addr,
    36  		mustConnect: mustConnect,
    37  	}
    38  	cli.BaseService = *cmn.NewBaseService(nil, "grpcClient", cli)
    39  	return cli
    40  }
    41  
    42  func dialerFunc(addr string, timeout time.Duration) (net.Conn, error) {
    43  	return cmn.Connect(addr)
    44  }
    45  
    46  func (cli *grpcClient) OnStart() error {
    47  	if err := cli.BaseService.OnStart(); err != nil {
    48  		return err
    49  	}
    50  RETRY_LOOP:
    51  	for {
    52  		conn, err := grpc.Dial(cli.addr, grpc.WithInsecure(), grpc.WithDialer(dialerFunc))
    53  		if err != nil {
    54  			if cli.mustConnect {
    55  				return err
    56  			}
    57  			cli.Logger.Error(fmt.Sprintf("abci.grpcClient failed to connect to %v.  Retrying...\n", cli.addr), "err", err)
    58  			time.Sleep(time.Second * dialRetryIntervalSeconds)
    59  			continue RETRY_LOOP
    60  		}
    61  
    62  		cli.Logger.Info("Dialed server. Waiting for echo.", "addr", cli.addr)
    63  		client := types.NewABCIApplicationClient(conn)
    64  		cli.conn = conn
    65  
    66  	ENSURE_CONNECTED:
    67  		for {
    68  			_, err := client.Echo(context.Background(), &types.RequestEcho{Message: "hello"}, grpc.FailFast(true))
    69  			if err == nil {
    70  				break ENSURE_CONNECTED
    71  			}
    72  			cli.Logger.Error("Echo failed", "err", err)
    73  			time.Sleep(time.Second * echoRetryIntervalSeconds)
    74  		}
    75  
    76  		cli.client = client
    77  		return nil
    78  	}
    79  }
    80  
    81  func (cli *grpcClient) OnStop() {
    82  	cli.BaseService.OnStop()
    83  
    84  	if cli.conn != nil {
    85  		cli.conn.Close()
    86  	}
    87  }
    88  
    89  func (cli *grpcClient) StopForError(err error) {
    90  	cli.mtx.Lock()
    91  	if !cli.IsRunning() {
    92  		return
    93  	}
    94  
    95  	if cli.err == nil {
    96  		cli.err = err
    97  	}
    98  	cli.mtx.Unlock()
    99  
   100  	cli.Logger.Error(fmt.Sprintf("Stopping abci.grpcClient for error: %v", err.Error()))
   101  	cli.Stop()
   102  }
   103  
   104  func (cli *grpcClient) Error() error {
   105  	cli.mtx.Lock()
   106  	defer cli.mtx.Unlock()
   107  	return cli.err
   108  }
   109  
   110  // Set listener for all responses
   111  // NOTE: callback may get internally generated flush responses.
   112  func (cli *grpcClient) SetResponseCallback(resCb Callback) {
   113  	cli.mtx.Lock()
   114  	cli.resCb = resCb
   115  	cli.mtx.Unlock()
   116  }
   117  
   118  //----------------------------------------
   119  // GRPC calls are synchronous, but some callbacks expect to be called asynchronously
   120  // (eg. the mempool expects to be able to lock to remove bad txs from cache).
   121  // To accommodate, we finish each call in its own go-routine,
   122  // which is expensive, but easy - if you want something better, use the socket protocol!
   123  // maybe one day, if people really want it, we use grpc streams,
   124  // but hopefully not :D
   125  
   126  func (cli *grpcClient) EchoAsync(msg string) *ReqRes {
   127  	req := types.ToRequestEcho(msg)
   128  	res, err := cli.client.Echo(context.Background(), req.GetEcho(), grpc.FailFast(true))
   129  	if err != nil {
   130  		cli.StopForError(err)
   131  	}
   132  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Echo{Echo: res}})
   133  }
   134  
   135  func (cli *grpcClient) FlushAsync() *ReqRes {
   136  	req := types.ToRequestFlush()
   137  	res, err := cli.client.Flush(context.Background(), req.GetFlush(), grpc.FailFast(true))
   138  	if err != nil {
   139  		cli.StopForError(err)
   140  	}
   141  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Flush{Flush: res}})
   142  }
   143  
   144  func (cli *grpcClient) InfoAsync(params types.RequestInfo) *ReqRes {
   145  	req := types.ToRequestInfo(params)
   146  	res, err := cli.client.Info(context.Background(), req.GetInfo(), grpc.FailFast(true))
   147  	if err != nil {
   148  		cli.StopForError(err)
   149  	}
   150  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Info{Info: res}})
   151  }
   152  
   153  func (cli *grpcClient) SetOptionAsync(params types.RequestSetOption) *ReqRes {
   154  	req := types.ToRequestSetOption(params)
   155  	res, err := cli.client.SetOption(context.Background(), req.GetSetOption(), grpc.FailFast(true))
   156  	if err != nil {
   157  		cli.StopForError(err)
   158  	}
   159  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_SetOption{SetOption: res}})
   160  }
   161  
   162  func (cli *grpcClient) DeliverTxAsync(params types.RequestDeliverTx) *ReqRes {
   163  	req := types.ToRequestDeliverTx(params)
   164  	res, err := cli.client.DeliverTx(context.Background(), req.GetDeliverTx(), grpc.FailFast(true))
   165  	if err != nil {
   166  		cli.StopForError(err)
   167  	}
   168  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_DeliverTx{DeliverTx: res}})
   169  }
   170  
   171  func (cli *grpcClient) CheckTxAsync(params types.RequestCheckTx) *ReqRes {
   172  	req := types.ToRequestCheckTx(params)
   173  	res, err := cli.client.CheckTx(context.Background(), req.GetCheckTx(), grpc.FailFast(true))
   174  	if err != nil {
   175  		cli.StopForError(err)
   176  	}
   177  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_CheckTx{CheckTx: res}})
   178  }
   179  
   180  func (cli *grpcClient) QueryAsync(params types.RequestQuery) *ReqRes {
   181  	req := types.ToRequestQuery(params)
   182  	res, err := cli.client.Query(context.Background(), req.GetQuery(), grpc.FailFast(true))
   183  	if err != nil {
   184  		cli.StopForError(err)
   185  	}
   186  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Query{Query: res}})
   187  }
   188  
   189  func (cli *grpcClient) CommitAsync() *ReqRes {
   190  	req := types.ToRequestCommit()
   191  	res, err := cli.client.Commit(context.Background(), req.GetCommit(), grpc.FailFast(true))
   192  	if err != nil {
   193  		cli.StopForError(err)
   194  	}
   195  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Commit{Commit: res}})
   196  }
   197  
   198  func (cli *grpcClient) InitChainAsync(params types.RequestInitChain) *ReqRes {
   199  	req := types.ToRequestInitChain(params)
   200  	res, err := cli.client.InitChain(context.Background(), req.GetInitChain(), grpc.FailFast(true))
   201  	if err != nil {
   202  		cli.StopForError(err)
   203  	}
   204  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_InitChain{InitChain: res}})
   205  }
   206  
   207  func (cli *grpcClient) BeginBlockAsync(params types.RequestBeginBlock) *ReqRes {
   208  	req := types.ToRequestBeginBlock(params)
   209  	res, err := cli.client.BeginBlock(context.Background(), req.GetBeginBlock(), grpc.FailFast(true))
   210  	if err != nil {
   211  		cli.StopForError(err)
   212  	}
   213  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_BeginBlock{BeginBlock: res}})
   214  }
   215  
   216  func (cli *grpcClient) EndBlockAsync(params types.RequestEndBlock) *ReqRes {
   217  	req := types.ToRequestEndBlock(params)
   218  	res, err := cli.client.EndBlock(context.Background(), req.GetEndBlock(), grpc.FailFast(true))
   219  	if err != nil {
   220  		cli.StopForError(err)
   221  	}
   222  	return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_EndBlock{EndBlock: res}})
   223  }
   224  
   225  func (cli *grpcClient) finishAsyncCall(req *types.Request, res *types.Response) *ReqRes {
   226  	reqres := NewReqRes(req)
   227  	reqres.Response = res // Set response
   228  	reqres.Done()         // Release waiters
   229  	reqres.SetDone()      // so reqRes.SetCallback will run the callback
   230  
   231  	// go routine for callbacks
   232  	go func() {
   233  		// Notify reqRes listener if set
   234  		if cb := reqres.GetCallback(); cb != nil {
   235  			cb(res)
   236  		}
   237  
   238  		// Notify client listener if set
   239  		if cli.resCb != nil {
   240  			cli.resCb(reqres.Request, res)
   241  		}
   242  	}()
   243  	return reqres
   244  }
   245  
   246  //----------------------------------------
   247  
   248  func (cli *grpcClient) FlushSync() error {
   249  	return nil
   250  }
   251  
   252  func (cli *grpcClient) EchoSync(msg string) (*types.ResponseEcho, error) {
   253  	reqres := cli.EchoAsync(msg)
   254  	// StopForError should already have been called if error is set
   255  	return reqres.Response.GetEcho(), cli.Error()
   256  }
   257  
   258  func (cli *grpcClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) {
   259  	reqres := cli.InfoAsync(req)
   260  	return reqres.Response.GetInfo(), cli.Error()
   261  }
   262  
   263  func (cli *grpcClient) SetOptionSync(req types.RequestSetOption) (*types.ResponseSetOption, error) {
   264  	reqres := cli.SetOptionAsync(req)
   265  	return reqres.Response.GetSetOption(), cli.Error()
   266  }
   267  
   268  func (cli *grpcClient) DeliverTxSync(params types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
   269  	reqres := cli.DeliverTxAsync(params)
   270  	return reqres.Response.GetDeliverTx(), cli.Error()
   271  }
   272  
   273  func (cli *grpcClient) CheckTxSync(params types.RequestCheckTx) (*types.ResponseCheckTx, error) {
   274  	reqres := cli.CheckTxAsync(params)
   275  	return reqres.Response.GetCheckTx(), cli.Error()
   276  }
   277  
   278  func (cli *grpcClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) {
   279  	reqres := cli.QueryAsync(req)
   280  	return reqres.Response.GetQuery(), cli.Error()
   281  }
   282  
   283  func (cli *grpcClient) CommitSync() (*types.ResponseCommit, error) {
   284  	reqres := cli.CommitAsync()
   285  	return reqres.Response.GetCommit(), cli.Error()
   286  }
   287  
   288  func (cli *grpcClient) InitChainSync(params types.RequestInitChain) (*types.ResponseInitChain, error) {
   289  	reqres := cli.InitChainAsync(params)
   290  	return reqres.Response.GetInitChain(), cli.Error()
   291  }
   292  
   293  func (cli *grpcClient) BeginBlockSync(params types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
   294  	reqres := cli.BeginBlockAsync(params)
   295  	return reqres.Response.GetBeginBlock(), cli.Error()
   296  }
   297  
   298  func (cli *grpcClient) EndBlockSync(params types.RequestEndBlock) (*types.ResponseEndBlock, error) {
   299  	reqres := cli.EndBlockAsync(params)
   300  	return reqres.Response.GetEndBlock(), cli.Error()
   301  }