You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

506 lines
16 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
7 years ago
8 years ago
8 years ago
abci: localClient improvements & bugfixes & pubsub Unsubscribe issues (#2748) * use READ lock/unlock in ConsensusState#GetLastHeight Refs #2721 * do not use defers when there's no need * fix peer formatting (output its address instead of the pointer) ``` [54310]: E[11-02|11:59:39.851] Connection failed @ sendRoutine module=p2p peer=0xb78f00 conn=MConn{74.207.236.148:26656} err="pong timeout" ``` https://github.com/tendermint/tendermint/issues/2721#issuecomment-435326581 * panic if peer has no state https://github.com/tendermint/tendermint/issues/2721#issuecomment-435347165 It's confusing that sometimes we check if peer has a state, but most of the times we expect it to be there 1. https://github.com/tendermint/tendermint/blob/add79700b5fe84417538202b6c927c8cc5383672/mempool/reactor.go#L138 2. https://github.com/tendermint/tendermint/blob/add79700b5fe84417538202b6c927c8cc5383672/rpc/core/consensus.go#L196 (edited) I will change everything to always assume peer has a state and panic otherwise that should help identify issues earlier * abci/localclient: extend lock on app callback App callback should be protected by lock as well (note this was already done for InitChainAsync, why not for others???). Otherwise, when we execute the block, tx might come in and call the callback in the same time we're updating it in execBlockOnProxyApp => DATA RACE Fixes #2721 Consensus state is locked ``` goroutine 113333 [semacquire, 309 minutes]: sync.runtime_SemacquireMutex(0xc00180009c, 0xc0000c7e00) /usr/local/go/src/runtime/sema.go:71 +0x3d sync.(*RWMutex).RLock(0xc001800090) /usr/local/go/src/sync/rwmutex.go:50 +0x4e github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusState).GetRoundState(0xc001800000, 0x0) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/state.go:218 +0x46 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusReactor).queryMaj23Routine(0xc0017def80, 0x11104a0, 0xc0072488f0, 0xc007248 9c0) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/reactor.go:735 +0x16d created by github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusReactor).AddPeer /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/reactor.go:172 +0x236 ``` because localClient is locked ``` goroutine 1899 [semacquire, 309 minutes]: sync.runtime_SemacquireMutex(0xc00003363c, 0xc0000cb500) /usr/local/go/src/runtime/sema.go:71 +0x3d sync.(*Mutex).Lock(0xc000033638) /usr/local/go/src/sync/mutex.go:134 +0xff github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/abci/client.(*localClient).SetResponseCallback(0xc0001fb560, 0xc007868540) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/abci/client/local_client.go:32 +0x33 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/proxy.(*appConnConsensus).SetResponseCallback(0xc00002f750, 0xc007868540) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/proxy/app_conn.go:57 +0x40 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/state.execBlockOnProxyApp(0x1104e20, 0xc002ca0ba0, 0x11092a0, 0xc00002f750, 0xc0001fe960, 0xc000bfc660, 0x110cfe0, 0xc000090330, 0xc9d12, 0xc000d9d5a0, ...) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/state/execution.go:230 +0x1fd github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/state.(*BlockExecutor).ApplyBlock(0xc002c2a230, 0x7, 0x0, 0xc000eae880, 0x6, 0xc002e52c60, 0x16, 0x1f927, 0xc9d12, 0xc000d9d5a0, ...) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/state/execution.go:96 +0x142 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusState).finalizeCommit(0xc001800000, 0x1f928) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/state.go:1339 +0xa3e github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusState).tryFinalizeCommit(0xc001800000, 0x1f928) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/state.go:1270 +0x451 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusState).enterCommit.func1(0xc001800000, 0x0, 0x1f928) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/state.go:1218 +0x90 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusState).enterCommit(0xc001800000, 0x1f928, 0x0) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/state.go:1247 +0x6b8 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusState).addVote(0xc001800000, 0xc003d8dea0, 0xc000cf4cc0, 0x28, 0xf1, 0xc003bc7ad0, 0xc003bc7b10) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/state.go:1659 +0xbad github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusState).tryAddVote(0xc001800000, 0xc003d8dea0, 0xc000cf4cc0, 0x28, 0xf1, 0xf1, 0xf1) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/state.go:1517 +0x59 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusState).handleMsg(0xc001800000, 0xd98200, 0xc0070dbed0, 0xc000cf4cc0, 0x28) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/state.go:660 +0x64b github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine(0xc001800000, 0x0) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/state.go:617 +0x670 created by github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus.(*ConsensusState).OnStart /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/consensus/state.go:311 +0x132 ``` tx comes in and CheckTx is executed right when we execute the block ``` goroutine 111044 [semacquire, 309 minutes]: sync.runtime_SemacquireMutex(0xc00003363c, 0x0) /usr/local/go/src/runtime/sema.go:71 +0x3d sync.(*Mutex).Lock(0xc000033638) /usr/local/go/src/sync/mutex.go:134 +0xff github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/abci/client.(*localClient).CheckTxAsync(0xc0001fb0e0, 0xc002d94500, 0x13f, 0x280, 0x0) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/abci/client/local_client.go:85 +0x47 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/proxy.(*appConnMempool).CheckTxAsync(0xc00002f720, 0xc002d94500, 0x13f, 0x280, 0x1) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/proxy/app_conn.go:114 +0x51 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/mempool.(*Mempool).CheckTx(0xc002d3a320, 0xc002d94500, 0x13f, 0x280, 0xc0072355f0, 0x0, 0x0) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/mempool/mempool.go:316 +0x17b github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/rpc/core.BroadcastTxSync(0xc002d94500, 0x13f, 0x280, 0x0, 0x0, 0x0) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/rpc/core/mempool.go:93 +0xb8 reflect.Value.call(0xd85560, 0x10326c0, 0x13, 0xec7b8b, 0x4, 0xc00663f180, 0x1, 0x1, 0xc00663f180, 0xc00663f188, ...) /usr/local/go/src/reflect/value.go:447 +0x449 reflect.Value.Call(0xd85560, 0x10326c0, 0x13, 0xc00663f180, 0x1, 0x1, 0x0, 0x0, 0xc005cc9344) /usr/local/go/src/reflect/value.go:308 +0xa4 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/rpc/lib/server.makeHTTPHandler.func2(0x1102060, 0xc00663f100, 0xc0082d7900) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/rpc/lib/server/handlers.go:269 +0x188 net/http.HandlerFunc.ServeHTTP(0xc002c81f20, 0x1102060, 0xc00663f100, 0xc0082d7900) /usr/local/go/src/net/http/server.go:1964 +0x44 net/http.(*ServeMux).ServeHTTP(0xc002c81b60, 0x1102060, 0xc00663f100, 0xc0082d7900) /usr/local/go/src/net/http/server.go:2361 +0x127 github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/rpc/lib/server.maxBytesHandler.ServeHTTP(0x10f8a40, 0xc002c81b60, 0xf4240, 0x1102060, 0xc00663f100, 0xc0082d7900) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/rpc/lib/server/http_server.go:219 +0xcf github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/rpc/lib/server.RecoverAndLogHandler.func1(0x1103220, 0xc00121e620, 0xc0082d7900) /root/go/src/github.com/MinterTeam/minter-go-node/vendor/github.com/tendermint/tendermint/rpc/lib/server/http_server.go:192 +0x394 net/http.HandlerFunc.ServeHTTP(0xc002c06ea0, 0x1103220, 0xc00121e620, 0xc0082d7900) /usr/local/go/src/net/http/server.go:1964 +0x44 net/http.serverHandler.ServeHTTP(0xc001a1aa90, 0x1103220, 0xc00121e620, 0xc0082d7900) /usr/local/go/src/net/http/server.go:2741 +0xab net/http.(*conn).serve(0xc00785a3c0, 0x11041a0, 0xc000f844c0) /usr/local/go/src/net/http/server.go:1847 +0x646 created by net/http.(*Server).Serve /usr/local/go/src/net/http/server.go:2851 +0x2f5 ``` * consensus: use read lock in Receive#VoteMessage * use defer to unlock mutex because application might panic * use defer in every method of the localClient * add a changelog entry * drain channels before Unsubscribe(All) Read https://github.com/tendermint/tendermint/blob/55362ed76630f3e1ebec159a598f6a9fb5892cb1/libs/pubsub/pubsub.go#L13 for the detailed explanation of the issue. We'll need to fix it someday. Make sure to keep an eye on https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-033-pubsub.md * retry instead of panic when peer has no state in reactors other than consensus in /dump_consensus_state RPC endpoint, skip a peer with no state * rpc/core/mempool: simplify error messages * rpc/core/mempool: use time.After instead of timer also, do not log DeliverTx result (to be consistent with other memthods) * unlock before calling the callback in reqRes#SetCallback
6 years ago
8 years ago
8 years ago
  1. package abcicli
  2. import (
  3. "context"
  4. "fmt"
  5. "net"
  6. "sync"
  7. "time"
  8. "google.golang.org/grpc"
  9. "github.com/tendermint/tendermint/abci/types"
  10. tmnet "github.com/tendermint/tendermint/libs/net"
  11. "github.com/tendermint/tendermint/libs/service"
  12. tmsync "github.com/tendermint/tendermint/libs/sync"
  13. )
  14. // A gRPC client.
  15. type grpcClient struct {
  16. service.BaseService
  17. mustConnect bool
  18. client types.ABCIApplicationClient
  19. conn *grpc.ClientConn
  20. chReqRes chan *ReqRes // dispatches "async" responses to callbacks *in order*, needed by mempool
  21. mtx tmsync.Mutex
  22. addr string
  23. err error
  24. resCb func(*types.Request, *types.Response) // listens to all callbacks
  25. }
  26. var _ Client = (*grpcClient)(nil)
  27. // NewGRPCClient creates a gRPC client, which will connect to addr upon the
  28. // start. Note Client#Start returns an error if connection is unsuccessful and
  29. // mustConnect is true.
  30. //
  31. // GRPC calls are synchronous, but some callbacks expect to be called
  32. // asynchronously (eg. the mempool expects to be able to lock to remove bad txs
  33. // from cache). To accommodate, we finish each call in its own go-routine,
  34. // which is expensive, but easy - if you want something better, use the socket
  35. // protocol! maybe one day, if people really want it, we use grpc streams, but
  36. // hopefully not :D
  37. func NewGRPCClient(addr string, mustConnect bool) Client {
  38. cli := &grpcClient{
  39. addr: addr,
  40. mustConnect: mustConnect,
  41. // Buffering the channel is needed to make calls appear asynchronous,
  42. // which is required when the caller makes multiple async calls before
  43. // processing callbacks (e.g. due to holding locks). 64 means that a
  44. // caller can make up to 64 async calls before a callback must be
  45. // processed (otherwise it deadlocks). It also means that we can make 64
  46. // gRPC calls while processing a slow callback at the channel head.
  47. chReqRes: make(chan *ReqRes, 64),
  48. }
  49. cli.BaseService = *service.NewBaseService(nil, "grpcClient", cli)
  50. return cli
  51. }
  52. func dialerFunc(ctx context.Context, addr string) (net.Conn, error) {
  53. return tmnet.Connect(addr)
  54. }
  55. func (cli *grpcClient) OnStart() error {
  56. // This processes asynchronous request/response messages and dispatches
  57. // them to callbacks.
  58. go func() {
  59. // Use a separate function to use defer for mutex unlocks (this handles panics)
  60. callCb := func(reqres *ReqRes) {
  61. cli.mtx.Lock()
  62. defer cli.mtx.Unlock()
  63. reqres.SetDone()
  64. reqres.Done()
  65. // Notify client listener if set
  66. if cli.resCb != nil {
  67. cli.resCb(reqres.Request, reqres.Response)
  68. }
  69. // Notify reqRes listener if set
  70. if cb := reqres.GetCallback(); cb != nil {
  71. cb(reqres.Response)
  72. }
  73. }
  74. for reqres := range cli.chReqRes {
  75. if reqres != nil {
  76. callCb(reqres)
  77. } else {
  78. cli.Logger.Error("Received nil reqres")
  79. }
  80. }
  81. }()
  82. RETRY_LOOP:
  83. for {
  84. conn, err := grpc.Dial(cli.addr, grpc.WithInsecure(), grpc.WithContextDialer(dialerFunc))
  85. if err != nil {
  86. if cli.mustConnect {
  87. return err
  88. }
  89. cli.Logger.Error(fmt.Sprintf("abci.grpcClient failed to connect to %v. Retrying...\n", cli.addr), "err", err)
  90. time.Sleep(time.Second * dialRetryIntervalSeconds)
  91. continue RETRY_LOOP
  92. }
  93. cli.Logger.Info("Dialed server. Waiting for echo.", "addr", cli.addr)
  94. client := types.NewABCIApplicationClient(conn)
  95. cli.conn = conn
  96. ENSURE_CONNECTED:
  97. for {
  98. _, err := client.Echo(context.Background(), &types.RequestEcho{Message: "hello"}, grpc.WaitForReady(true))
  99. if err == nil {
  100. break ENSURE_CONNECTED
  101. }
  102. cli.Logger.Error("Echo failed", "err", err)
  103. time.Sleep(time.Second * echoRetryIntervalSeconds)
  104. }
  105. cli.client = client
  106. return nil
  107. }
  108. }
  109. func (cli *grpcClient) OnStop() {
  110. if cli.conn != nil {
  111. cli.conn.Close()
  112. }
  113. close(cli.chReqRes)
  114. }
  115. func (cli *grpcClient) StopForError(err error) {
  116. if !cli.IsRunning() {
  117. return
  118. }
  119. cli.mtx.Lock()
  120. if cli.err == nil {
  121. cli.err = err
  122. }
  123. cli.mtx.Unlock()
  124. cli.Logger.Error(fmt.Sprintf("Stopping abci.grpcClient for error: %v", err.Error()))
  125. if err := cli.Stop(); err != nil {
  126. cli.Logger.Error("Error stopping abci.grpcClient", "err", err)
  127. }
  128. }
  129. func (cli *grpcClient) Error() error {
  130. cli.mtx.Lock()
  131. defer cli.mtx.Unlock()
  132. return cli.err
  133. }
  134. // Set listener for all responses
  135. // NOTE: callback may get internally generated flush responses.
  136. func (cli *grpcClient) SetResponseCallback(resCb Callback) {
  137. cli.mtx.Lock()
  138. cli.resCb = resCb
  139. cli.mtx.Unlock()
  140. }
  141. //----------------------------------------
  142. // NOTE: call is synchronous, use ctx to break early if needed
  143. func (cli *grpcClient) EchoAsync(ctx context.Context, msg string) (*ReqRes, error) {
  144. req := types.ToRequestEcho(msg)
  145. res, err := cli.client.Echo(ctx, req.GetEcho(), grpc.WaitForReady(true))
  146. if err != nil {
  147. return nil, err
  148. }
  149. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_Echo{Echo: res}})
  150. }
  151. // NOTE: call is synchronous, use ctx to break early if needed
  152. func (cli *grpcClient) FlushAsync(ctx context.Context) (*ReqRes, error) {
  153. req := types.ToRequestFlush()
  154. res, err := cli.client.Flush(ctx, req.GetFlush(), grpc.WaitForReady(true))
  155. if err != nil {
  156. return nil, err
  157. }
  158. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_Flush{Flush: res}})
  159. }
  160. // NOTE: call is synchronous, use ctx to break early if needed
  161. func (cli *grpcClient) InfoAsync(ctx context.Context, params types.RequestInfo) (*ReqRes, error) {
  162. req := types.ToRequestInfo(params)
  163. res, err := cli.client.Info(ctx, req.GetInfo(), grpc.WaitForReady(true))
  164. if err != nil {
  165. return nil, err
  166. }
  167. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_Info{Info: res}})
  168. }
  169. // NOTE: call is synchronous, use ctx to break early if needed
  170. func (cli *grpcClient) DeliverTxAsync(ctx context.Context, params types.RequestDeliverTx) (*ReqRes, error) {
  171. req := types.ToRequestDeliverTx(params)
  172. res, err := cli.client.DeliverTx(ctx, req.GetDeliverTx(), grpc.WaitForReady(true))
  173. if err != nil {
  174. return nil, err
  175. }
  176. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_DeliverTx{DeliverTx: res}})
  177. }
  178. // NOTE: call is synchronous, use ctx to break early if needed
  179. func (cli *grpcClient) CheckTxAsync(ctx context.Context, params types.RequestCheckTx) (*ReqRes, error) {
  180. req := types.ToRequestCheckTx(params)
  181. res, err := cli.client.CheckTx(ctx, req.GetCheckTx(), grpc.WaitForReady(true))
  182. if err != nil {
  183. return nil, err
  184. }
  185. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_CheckTx{CheckTx: res}})
  186. }
  187. // NOTE: call is synchronous, use ctx to break early if needed
  188. func (cli *grpcClient) QueryAsync(ctx context.Context, params types.RequestQuery) (*ReqRes, error) {
  189. req := types.ToRequestQuery(params)
  190. res, err := cli.client.Query(ctx, req.GetQuery(), grpc.WaitForReady(true))
  191. if err != nil {
  192. return nil, err
  193. }
  194. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_Query{Query: res}})
  195. }
  196. // NOTE: call is synchronous, use ctx to break early if needed
  197. func (cli *grpcClient) CommitAsync(ctx context.Context) (*ReqRes, error) {
  198. req := types.ToRequestCommit()
  199. res, err := cli.client.Commit(ctx, req.GetCommit(), grpc.WaitForReady(true))
  200. if err != nil {
  201. return nil, err
  202. }
  203. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_Commit{Commit: res}})
  204. }
  205. // NOTE: call is synchronous, use ctx to break early if needed
  206. func (cli *grpcClient) InitChainAsync(ctx context.Context, params types.RequestInitChain) (*ReqRes, error) {
  207. req := types.ToRequestInitChain(params)
  208. res, err := cli.client.InitChain(ctx, req.GetInitChain(), grpc.WaitForReady(true))
  209. if err != nil {
  210. return nil, err
  211. }
  212. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_InitChain{InitChain: res}})
  213. }
  214. // NOTE: call is synchronous, use ctx to break early if needed
  215. func (cli *grpcClient) BeginBlockAsync(ctx context.Context, params types.RequestBeginBlock) (*ReqRes, error) {
  216. req := types.ToRequestBeginBlock(params)
  217. res, err := cli.client.BeginBlock(ctx, req.GetBeginBlock(), grpc.WaitForReady(true))
  218. if err != nil {
  219. return nil, err
  220. }
  221. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_BeginBlock{BeginBlock: res}})
  222. }
  223. // NOTE: call is synchronous, use ctx to break early if needed
  224. func (cli *grpcClient) EndBlockAsync(ctx context.Context, params types.RequestEndBlock) (*ReqRes, error) {
  225. req := types.ToRequestEndBlock(params)
  226. res, err := cli.client.EndBlock(ctx, req.GetEndBlock(), grpc.WaitForReady(true))
  227. if err != nil {
  228. return nil, err
  229. }
  230. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_EndBlock{EndBlock: res}})
  231. }
  232. // NOTE: call is synchronous, use ctx to break early if needed
  233. func (cli *grpcClient) ListSnapshotsAsync(ctx context.Context, params types.RequestListSnapshots) (*ReqRes, error) {
  234. req := types.ToRequestListSnapshots(params)
  235. res, err := cli.client.ListSnapshots(ctx, req.GetListSnapshots(), grpc.WaitForReady(true))
  236. if err != nil {
  237. return nil, err
  238. }
  239. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_ListSnapshots{ListSnapshots: res}})
  240. }
  241. // NOTE: call is synchronous, use ctx to break early if needed
  242. func (cli *grpcClient) OfferSnapshotAsync(ctx context.Context, params types.RequestOfferSnapshot) (*ReqRes, error) {
  243. req := types.ToRequestOfferSnapshot(params)
  244. res, err := cli.client.OfferSnapshot(ctx, req.GetOfferSnapshot(), grpc.WaitForReady(true))
  245. if err != nil {
  246. return nil, err
  247. }
  248. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_OfferSnapshot{OfferSnapshot: res}})
  249. }
  250. // NOTE: call is synchronous, use ctx to break early if needed
  251. func (cli *grpcClient) LoadSnapshotChunkAsync(
  252. ctx context.Context,
  253. params types.RequestLoadSnapshotChunk,
  254. ) (*ReqRes, error) {
  255. req := types.ToRequestLoadSnapshotChunk(params)
  256. res, err := cli.client.LoadSnapshotChunk(ctx, req.GetLoadSnapshotChunk(), grpc.WaitForReady(true))
  257. if err != nil {
  258. return nil, err
  259. }
  260. return cli.finishAsyncCall(ctx, req, &types.Response{Value: &types.Response_LoadSnapshotChunk{LoadSnapshotChunk: res}})
  261. }
  262. // NOTE: call is synchronous, use ctx to break early if needed
  263. func (cli *grpcClient) ApplySnapshotChunkAsync(
  264. ctx context.Context,
  265. params types.RequestApplySnapshotChunk,
  266. ) (*ReqRes, error) {
  267. req := types.ToRequestApplySnapshotChunk(params)
  268. res, err := cli.client.ApplySnapshotChunk(ctx, req.GetApplySnapshotChunk(), grpc.WaitForReady(true))
  269. if err != nil {
  270. return nil, err
  271. }
  272. return cli.finishAsyncCall(
  273. ctx,
  274. req,
  275. &types.Response{Value: &types.Response_ApplySnapshotChunk{ApplySnapshotChunk: res}},
  276. )
  277. }
  278. // finishAsyncCall creates a ReqRes for an async call, and immediately populates it
  279. // with the response. We don't complete it until it's been ordered via the channel.
  280. func (cli *grpcClient) finishAsyncCall(ctx context.Context, req *types.Request, res *types.Response) (*ReqRes, error) {
  281. reqres := NewReqRes(req)
  282. reqres.Response = res
  283. select {
  284. case cli.chReqRes <- reqres: // use channel for async responses, since they must be ordered
  285. return reqres, nil
  286. case <-ctx.Done():
  287. return nil, ctx.Err()
  288. }
  289. }
  290. // finishSyncCall waits for an async call to complete. It is necessary to call all
  291. // sync calls asynchronously as well, to maintain call and response ordering via
  292. // the channel, and this method will wait until the async call completes.
  293. func (cli *grpcClient) finishSyncCall(reqres *ReqRes) *types.Response {
  294. // It's possible that the callback is called twice, since the callback can
  295. // be called immediately on SetCallback() in addition to after it has been
  296. // set. This is because completing the ReqRes happens in a separate critical
  297. // section from the one where the callback is called: there is a race where
  298. // SetCallback() is called between completing the ReqRes and dispatching the
  299. // callback.
  300. //
  301. // We also buffer the channel with 1 response, since SetCallback() will be
  302. // called synchronously if the reqres is already completed, in which case
  303. // it will block on sending to the channel since it hasn't gotten around to
  304. // receiving from it yet.
  305. //
  306. // ReqRes should really handle callback dispatch internally, to guarantee
  307. // that it's only called once and avoid the above race conditions.
  308. var once sync.Once
  309. ch := make(chan *types.Response, 1)
  310. reqres.SetCallback(func(res *types.Response) {
  311. once.Do(func() {
  312. ch <- res
  313. })
  314. })
  315. return <-ch
  316. }
  317. //----------------------------------------
  318. func (cli *grpcClient) FlushSync(ctx context.Context) error {
  319. return nil
  320. }
  321. func (cli *grpcClient) EchoSync(ctx context.Context, msg string) (*types.ResponseEcho, error) {
  322. reqres, err := cli.EchoAsync(ctx, msg)
  323. if err != nil {
  324. return nil, err
  325. }
  326. return cli.finishSyncCall(reqres).GetEcho(), cli.Error()
  327. }
  328. func (cli *grpcClient) InfoSync(
  329. ctx context.Context,
  330. req types.RequestInfo,
  331. ) (*types.ResponseInfo, error) {
  332. reqres, err := cli.InfoAsync(ctx, req)
  333. if err != nil {
  334. return nil, err
  335. }
  336. return cli.finishSyncCall(reqres).GetInfo(), cli.Error()
  337. }
  338. func (cli *grpcClient) DeliverTxSync(
  339. ctx context.Context,
  340. params types.RequestDeliverTx,
  341. ) (*types.ResponseDeliverTx, error) {
  342. reqres, err := cli.DeliverTxAsync(ctx, params)
  343. if err != nil {
  344. return nil, err
  345. }
  346. return cli.finishSyncCall(reqres).GetDeliverTx(), cli.Error()
  347. }
  348. func (cli *grpcClient) CheckTxSync(
  349. ctx context.Context,
  350. params types.RequestCheckTx,
  351. ) (*types.ResponseCheckTx, error) {
  352. reqres, err := cli.CheckTxAsync(ctx, params)
  353. if err != nil {
  354. return nil, err
  355. }
  356. return cli.finishSyncCall(reqres).GetCheckTx(), cli.Error()
  357. }
  358. func (cli *grpcClient) QuerySync(
  359. ctx context.Context,
  360. req types.RequestQuery,
  361. ) (*types.ResponseQuery, error) {
  362. reqres, err := cli.QueryAsync(ctx, req)
  363. if err != nil {
  364. return nil, err
  365. }
  366. return cli.finishSyncCall(reqres).GetQuery(), cli.Error()
  367. }
  368. func (cli *grpcClient) CommitSync(ctx context.Context) (*types.ResponseCommit, error) {
  369. reqres, err := cli.CommitAsync(ctx)
  370. if err != nil {
  371. return nil, err
  372. }
  373. return cli.finishSyncCall(reqres).GetCommit(), cli.Error()
  374. }
  375. func (cli *grpcClient) InitChainSync(
  376. ctx context.Context,
  377. params types.RequestInitChain,
  378. ) (*types.ResponseInitChain, error) {
  379. reqres, err := cli.InitChainAsync(ctx, params)
  380. if err != nil {
  381. return nil, err
  382. }
  383. return cli.finishSyncCall(reqres).GetInitChain(), cli.Error()
  384. }
  385. func (cli *grpcClient) BeginBlockSync(
  386. ctx context.Context,
  387. params types.RequestBeginBlock,
  388. ) (*types.ResponseBeginBlock, error) {
  389. reqres, err := cli.BeginBlockAsync(ctx, params)
  390. if err != nil {
  391. return nil, err
  392. }
  393. return cli.finishSyncCall(reqres).GetBeginBlock(), cli.Error()
  394. }
  395. func (cli *grpcClient) EndBlockSync(
  396. ctx context.Context,
  397. params types.RequestEndBlock,
  398. ) (*types.ResponseEndBlock, error) {
  399. reqres, err := cli.EndBlockAsync(ctx, params)
  400. if err != nil {
  401. return nil, err
  402. }
  403. return cli.finishSyncCall(reqres).GetEndBlock(), cli.Error()
  404. }
  405. func (cli *grpcClient) ListSnapshotsSync(
  406. ctx context.Context,
  407. params types.RequestListSnapshots,
  408. ) (*types.ResponseListSnapshots, error) {
  409. reqres, err := cli.ListSnapshotsAsync(ctx, params)
  410. if err != nil {
  411. return nil, err
  412. }
  413. return cli.finishSyncCall(reqres).GetListSnapshots(), cli.Error()
  414. }
  415. func (cli *grpcClient) OfferSnapshotSync(
  416. ctx context.Context,
  417. params types.RequestOfferSnapshot,
  418. ) (*types.ResponseOfferSnapshot, error) {
  419. reqres, err := cli.OfferSnapshotAsync(ctx, params)
  420. if err != nil {
  421. return nil, err
  422. }
  423. return cli.finishSyncCall(reqres).GetOfferSnapshot(), cli.Error()
  424. }
  425. func (cli *grpcClient) LoadSnapshotChunkSync(
  426. ctx context.Context,
  427. params types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
  428. reqres, err := cli.LoadSnapshotChunkAsync(ctx, params)
  429. if err != nil {
  430. return nil, err
  431. }
  432. return cli.finishSyncCall(reqres).GetLoadSnapshotChunk(), cli.Error()
  433. }
  434. func (cli *grpcClient) ApplySnapshotChunkSync(
  435. ctx context.Context,
  436. params types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
  437. reqres, err := cli.ApplySnapshotChunkAsync(ctx, params)
  438. if err != nil {
  439. return nil, err
  440. }
  441. return cli.finishSyncCall(reqres).GetApplySnapshotChunk(), cli.Error()
  442. }