github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/abci/client/socket_client.go (about) 1 package abciclient 2 3 import ( 4 "bufio" 5 "container/list" 6 "context" 7 "errors" 8 "fmt" 9 "io" 10 "net" 11 "sync" 12 "time" 13 14 "github.com/ari-anchor/sei-tendermint/abci/types" 15 "github.com/ari-anchor/sei-tendermint/libs/log" 16 tmnet "github.com/ari-anchor/sei-tendermint/libs/net" 17 "github.com/ari-anchor/sei-tendermint/libs/service" 18 ) 19 20 // This is goroutine-safe, but users should beware that the application in 21 // general is not meant to be interfaced with concurrent callers. 22 type socketClient struct { 23 service.BaseService 24 logger log.Logger 25 26 addr string 27 mustConnect bool 28 conn net.Conn 29 30 reqQueue chan *requestAndResponse 31 32 mtx sync.Mutex 33 err error 34 reqSent *list.List // list of requests sent, waiting for response 35 } 36 37 var _ Client = (*socketClient)(nil) 38 39 // NewSocketClient creates a new socket client, which connects to a given 40 // address. If mustConnect is true, the client will return an error upon start 41 // if it fails to connect. 42 func NewSocketClient(logger log.Logger, addr string, mustConnect bool) Client { 43 cli := &socketClient{ 44 logger: logger, 45 reqQueue: make(chan *requestAndResponse), 46 mustConnect: mustConnect, 47 addr: addr, 48 reqSent: list.New(), 49 } 50 cli.BaseService = *service.NewBaseService(logger, "socketClient", cli) 51 return cli 52 } 53 54 // OnStart implements Service by connecting to the server and spawning reading 55 // and writing goroutines. 56 func (cli *socketClient) OnStart(ctx context.Context) error { 57 var ( 58 err error 59 conn net.Conn 60 ) 61 timer := time.NewTimer(0) 62 defer timer.Stop() 63 64 for { 65 conn, err = tmnet.Connect(cli.addr) 66 if err != nil { 67 if cli.mustConnect { 68 return err 69 } 70 cli.logger.Error(fmt.Sprintf("abci.socketClient failed to connect to %v. Retrying after %vs...", 71 cli.addr, dialRetryIntervalSeconds), "err", err) 72 73 timer.Reset(time.Second * dialRetryIntervalSeconds) 74 select { 75 case <-ctx.Done(): 76 return ctx.Err() 77 case <-timer.C: 78 continue 79 } 80 81 } 82 cli.conn = conn 83 84 go cli.sendRequestsRoutine(ctx, conn) 85 go cli.recvResponseRoutine(ctx, conn) 86 87 return nil 88 } 89 } 90 91 // OnStop implements Service by closing connection and flushing all queues. 92 func (cli *socketClient) OnStop() { 93 if cli.conn != nil { 94 cli.conn.Close() 95 } 96 cli.drainQueue() 97 } 98 99 // Error returns an error if the client was stopped abruptly. 100 func (cli *socketClient) Error() error { 101 cli.mtx.Lock() 102 defer cli.mtx.Unlock() 103 return cli.err 104 } 105 106 //---------------------------------------- 107 108 func (cli *socketClient) sendRequestsRoutine(ctx context.Context, conn io.Writer) { 109 bw := bufio.NewWriter(conn) 110 for { 111 select { 112 case <-ctx.Done(): 113 return 114 case reqres := <-cli.reqQueue: 115 // N.B. We must enqueue before sending out the request, otherwise the 116 // server may reply before we do it, and the receiver will fail for an 117 // unsolicited reply. 118 cli.trackRequest(reqres) 119 120 if err := types.WriteMessage(reqres.Request, bw); err != nil { 121 cli.stopForError(fmt.Errorf("write to buffer: %w", err)) 122 return 123 } 124 125 if err := bw.Flush(); err != nil { 126 cli.stopForError(fmt.Errorf("flush buffer: %w", err)) 127 return 128 } 129 } 130 } 131 } 132 133 func (cli *socketClient) recvResponseRoutine(ctx context.Context, conn io.Reader) { 134 r := bufio.NewReader(conn) 135 for { 136 if ctx.Err() != nil { 137 return 138 } 139 res := &types.Response{} 140 141 if err := types.ReadMessage(r, res); err != nil { 142 cli.stopForError(fmt.Errorf("read message: %w", err)) 143 return 144 } 145 146 switch r := res.Value.(type) { 147 case *types.Response_Exception: // app responded with error 148 // XXX After setting cli.err, release waiters (e.g. reqres.Done()) 149 cli.stopForError(errors.New(r.Exception.Error)) 150 return 151 default: 152 if err := cli.didRecvResponse(res); err != nil { 153 cli.stopForError(err) 154 return 155 } 156 } 157 } 158 } 159 160 func (cli *socketClient) trackRequest(reqres *requestAndResponse) { 161 // N.B. We must NOT hold the client state lock while checking this, or we 162 // may deadlock with shutdown. 163 if !cli.IsRunning() { 164 return 165 } 166 167 cli.mtx.Lock() 168 defer cli.mtx.Unlock() 169 cli.reqSent.PushBack(reqres) 170 } 171 172 func (cli *socketClient) didRecvResponse(res *types.Response) error { 173 cli.mtx.Lock() 174 defer cli.mtx.Unlock() 175 176 // Get the first ReqRes. 177 next := cli.reqSent.Front() 178 if next == nil { 179 return fmt.Errorf("unexpected %T when nothing expected", res.Value) 180 } 181 182 reqres := next.Value.(*requestAndResponse) 183 if !resMatchesReq(reqres.Request, res) { 184 return fmt.Errorf("unexpected %T when response to %T expected", res.Value, reqres.Request.Value) 185 } 186 187 reqres.Response = res 188 reqres.markDone() // release waiters 189 cli.reqSent.Remove(next) // pop first item from linked list 190 191 return nil 192 } 193 194 //---------------------------------------- 195 196 func (cli *socketClient) doRequest(ctx context.Context, req *types.Request) (*types.Response, error) { 197 if !cli.IsRunning() { 198 return nil, errors.New("client has stopped") 199 } 200 201 reqres := makeReqRes(req) 202 203 select { 204 case cli.reqQueue <- reqres: 205 case <-ctx.Done(): 206 return nil, fmt.Errorf("can't queue req: %w", ctx.Err()) 207 } 208 209 select { 210 case <-reqres.signal: 211 if err := cli.Error(); err != nil { 212 return nil, err 213 } 214 215 return reqres.Response, nil 216 case <-ctx.Done(): 217 return nil, ctx.Err() 218 } 219 } 220 221 // drainQueue marks as complete and discards all remaining pending requests 222 // from the queue. 223 func (cli *socketClient) drainQueue() { 224 cli.mtx.Lock() 225 defer cli.mtx.Unlock() 226 227 // mark all in-flight messages as resolved (they will get cli.Error()) 228 for req := cli.reqSent.Front(); req != nil; req = req.Next() { 229 reqres := req.Value.(*requestAndResponse) 230 reqres.markDone() 231 } 232 } 233 234 //---------------------------------------- 235 236 func (cli *socketClient) Flush(ctx context.Context) error { 237 _, err := cli.doRequest(ctx, types.ToRequestFlush()) 238 if err != nil { 239 return err 240 } 241 return nil 242 } 243 244 func (cli *socketClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) { 245 res, err := cli.doRequest(ctx, types.ToRequestEcho(msg)) 246 if err != nil { 247 return nil, err 248 } 249 return res.GetEcho(), nil 250 } 251 252 func (cli *socketClient) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) { 253 res, err := cli.doRequest(ctx, types.ToRequestInfo(req)) 254 if err != nil { 255 return nil, err 256 } 257 return res.GetInfo(), nil 258 } 259 260 func (cli *socketClient) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) { 261 res, err := cli.doRequest(ctx, types.ToRequestCheckTx(req)) 262 if err != nil { 263 return nil, err 264 } 265 return res.GetCheckTx(), nil 266 } 267 268 func (cli *socketClient) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) { 269 res, err := cli.doRequest(ctx, types.ToRequestQuery(req)) 270 if err != nil { 271 return nil, err 272 } 273 return res.GetQuery(), nil 274 } 275 276 func (cli *socketClient) Commit(ctx context.Context) (*types.ResponseCommit, error) { 277 res, err := cli.doRequest(ctx, types.ToRequestCommit()) 278 if err != nil { 279 return nil, err 280 } 281 return res.GetCommit(), nil 282 } 283 284 func (cli *socketClient) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) { 285 res, err := cli.doRequest(ctx, types.ToRequestInitChain(req)) 286 if err != nil { 287 return nil, err 288 } 289 return res.GetInitChain(), nil 290 } 291 292 func (cli *socketClient) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { 293 res, err := cli.doRequest(ctx, types.ToRequestListSnapshots(req)) 294 if err != nil { 295 return nil, err 296 } 297 return res.GetListSnapshots(), nil 298 } 299 300 func (cli *socketClient) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { 301 res, err := cli.doRequest(ctx, types.ToRequestOfferSnapshot(req)) 302 if err != nil { 303 return nil, err 304 } 305 return res.GetOfferSnapshot(), nil 306 } 307 308 func (cli *socketClient) LoadSnapshotChunk(ctx context.Context, req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { 309 res, err := cli.doRequest(ctx, types.ToRequestLoadSnapshotChunk(req)) 310 if err != nil { 311 return nil, err 312 } 313 return res.GetLoadSnapshotChunk(), nil 314 } 315 316 func (cli *socketClient) ApplySnapshotChunk(ctx context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { 317 res, err := cli.doRequest(ctx, types.ToRequestApplySnapshotChunk(req)) 318 if err != nil { 319 return nil, err 320 } 321 return res.GetApplySnapshotChunk(), nil 322 } 323 324 func (cli *socketClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { 325 res, err := cli.doRequest(ctx, types.ToRequestPrepareProposal(req)) 326 if err != nil { 327 return nil, err 328 } 329 return res.GetPrepareProposal(), nil 330 } 331 332 func (cli *socketClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { 333 res, err := cli.doRequest(ctx, types.ToRequestProcessProposal(req)) 334 if err != nil { 335 return nil, err 336 } 337 return res.GetProcessProposal(), nil 338 } 339 340 func (cli *socketClient) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) { 341 res, err := cli.doRequest(ctx, types.ToRequestExtendVote(req)) 342 if err != nil { 343 return nil, err 344 } 345 return res.GetExtendVote(), nil 346 } 347 348 func (cli *socketClient) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) { 349 res, err := cli.doRequest(ctx, types.ToRequestVerifyVoteExtension(req)) 350 if err != nil { 351 return nil, err 352 } 353 return res.GetVerifyVoteExtension(), nil 354 } 355 356 func (cli *socketClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { 357 res, err := cli.doRequest(ctx, types.ToRequestFinalizeBlock(req)) 358 if err != nil { 359 return nil, err 360 } 361 return res.GetFinalizeBlock(), nil 362 } 363 364 func (cli *socketClient) LoadLatest(ctx context.Context, req *types.RequestLoadLatest) (*types.ResponseLoadLatest, error) { 365 res, err := cli.doRequest(ctx, types.ToRequestLoadLatest(req)) 366 if err != nil { 367 return nil, err 368 } 369 return res.GetLoadLatest(), nil 370 } 371 372 //---------------------------------------- 373 374 func resMatchesReq(req *types.Request, res *types.Response) (ok bool) { 375 switch req.Value.(type) { 376 case *types.Request_Echo: 377 _, ok = res.Value.(*types.Response_Echo) 378 case *types.Request_Flush: 379 _, ok = res.Value.(*types.Response_Flush) 380 case *types.Request_Info: 381 _, ok = res.Value.(*types.Response_Info) 382 case *types.Request_CheckTx: 383 _, ok = res.Value.(*types.Response_CheckTx) 384 case *types.Request_Commit: 385 _, ok = res.Value.(*types.Response_Commit) 386 case *types.Request_Query: 387 _, ok = res.Value.(*types.Response_Query) 388 case *types.Request_InitChain: 389 _, ok = res.Value.(*types.Response_InitChain) 390 case *types.Request_ProcessProposal: 391 _, ok = res.Value.(*types.Response_ProcessProposal) 392 case *types.Request_PrepareProposal: 393 _, ok = res.Value.(*types.Response_PrepareProposal) 394 case *types.Request_ExtendVote: 395 _, ok = res.Value.(*types.Response_ExtendVote) 396 case *types.Request_VerifyVoteExtension: 397 _, ok = res.Value.(*types.Response_VerifyVoteExtension) 398 case *types.Request_ApplySnapshotChunk: 399 _, ok = res.Value.(*types.Response_ApplySnapshotChunk) 400 case *types.Request_LoadSnapshotChunk: 401 _, ok = res.Value.(*types.Response_LoadSnapshotChunk) 402 case *types.Request_ListSnapshots: 403 _, ok = res.Value.(*types.Response_ListSnapshots) 404 case *types.Request_OfferSnapshot: 405 _, ok = res.Value.(*types.Response_OfferSnapshot) 406 case *types.Request_FinalizeBlock: 407 _, ok = res.Value.(*types.Response_FinalizeBlock) 408 } 409 return ok 410 } 411 412 func (cli *socketClient) stopForError(err error) { 413 if !cli.IsRunning() { 414 return 415 } 416 417 cli.mtx.Lock() 418 cli.err = err 419 cli.mtx.Unlock() 420 421 cli.logger.Info("Stopping abci.socketClient", "reason", err) 422 cli.Stop() 423 }