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.

613 lines
16 KiB

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