github.com/okex/exchain@v1.8.0/libs/tendermint/rpc/client/http/http.go (about) 1 package http 2 3 import ( 4 "context" 5 "crypto/sha256" 6 "net/http" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/pkg/errors" 12 13 amino "github.com/tendermint/go-amino" 14 15 "github.com/okex/exchain/libs/tendermint/libs/bytes" 16 "github.com/okex/exchain/libs/tendermint/libs/log" 17 tmpubsub "github.com/okex/exchain/libs/tendermint/libs/pubsub" 18 "github.com/okex/exchain/libs/tendermint/libs/service" 19 rpcclient "github.com/okex/exchain/libs/tendermint/rpc/client" 20 ctypes "github.com/okex/exchain/libs/tendermint/rpc/core/types" 21 jsonrpcclient "github.com/okex/exchain/libs/tendermint/rpc/jsonrpc/client" 22 "github.com/okex/exchain/libs/tendermint/types" 23 ) 24 25 /* 26 HTTP is a Client implementation that communicates with a Tendermint node over 27 JSON RPC and WebSockets. 28 29 This is the main implementation you probably want to use in production code. 30 There are other implementations when calling the Tendermint node in-process 31 (Local), or when you want to mock out the server for test code (mock). 32 33 You can subscribe for any event published by Tendermint using Subscribe method. 34 Note delivery is best-effort. If you don't read events fast enough or network is 35 slow, Tendermint might cancel the subscription. The client will attempt to 36 resubscribe (you don't need to do anything). It will keep trying every second 37 indefinitely until successful. 38 39 Request batching is available for JSON RPC requests over HTTP, which conforms to 40 the JSON RPC specification (https://www.jsonrpc.org/specification#batch). See 41 the example for more details. 42 43 Example: 44 45 c, err := New("http://192.168.1.10:26657", "/websocket") 46 if err != nil { 47 // handle error 48 } 49 50 // call Start/Stop if you're subscribing to events 51 err = c.Start() 52 if err != nil { 53 // handle error 54 } 55 defer c.Stop() 56 57 res, err := c.Status() 58 if err != nil { 59 // handle error 60 } 61 62 // handle result 63 */ 64 type HTTP struct { 65 remote string 66 rpc *jsonrpcclient.Cm39HttpJSONClientAdapter 67 68 *baseRPCClient 69 *WSEvents 70 } 71 72 func (c *HTTP) GetUnconfirmedTxByHash(hash [sha256.Size]byte) (types.Tx, error) { 73 return nil, errors.New("unhandled method") 74 } 75 76 // BatchHTTP provides the same interface as `HTTP`, but allows for batching of 77 // requests (as per https://www.jsonrpc.org/specification#batch). Do not 78 // instantiate directly - rather use the HTTP.NewBatch() method to create an 79 // instance of this struct. 80 // 81 // Batching of HTTP requests is thread-safe in the sense that multiple 82 // goroutines can each create their own batches and send them using the same 83 // HTTP client. Multiple goroutines could also enqueue transactions in a single 84 // batch, but ordering of transactions in the batch cannot be guaranteed in such 85 // an example. 86 type BatchHTTP struct { 87 rpcBatch *jsonrpcclient.RequestBatch 88 *baseRPCClient 89 } 90 91 // rpcClient is an internal interface to which our RPC clients (batch and 92 // non-batch) must conform. Acts as an additional code-level sanity check to 93 // make sure the implementations stay coherent. 94 type rpcClient interface { 95 rpcclient.ABCIClient 96 rpcclient.HistoryClient 97 rpcclient.NetworkClient 98 rpcclient.SignClient 99 rpcclient.StatusClient 100 } 101 102 // baseRPCClient implements the basic RPC method logic without the actual 103 // underlying RPC call functionality, which is provided by `caller`. 104 type baseRPCClient struct { 105 caller jsonrpcclient.Caller 106 } 107 108 var _ rpcClient = (*HTTP)(nil) 109 var _ rpcClient = (*BatchHTTP)(nil) 110 var _ rpcClient = (*baseRPCClient)(nil) 111 112 //----------------------------------------------------------------------------- 113 // HTTP 114 115 // New takes a remote endpoint in the form <protocol>://<host>:<port> and 116 // the websocket path (which always seems to be "/websocket") 117 // An error is returned on invalid remote. The function panics when remote is nil. 118 func New(remote, wsEndpoint string) (*HTTP, error) { 119 httpClient, err := jsonrpcclient.DefaultHTTPClient(remote) 120 if err != nil { 121 return nil, err 122 } 123 return NewWithClient(remote, wsEndpoint, httpClient) 124 } 125 126 // Create timeout enabled http client 127 func NewWithTimeout(remote, wsEndpoint string, timeout uint) (*HTTP, error) { 128 httpClient, err := jsonrpcclient.DefaultHTTPClient(remote) 129 if err != nil { 130 return nil, err 131 } 132 httpClient.Timeout = time.Duration(timeout) * time.Second 133 return NewWithClient(remote, wsEndpoint, httpClient) 134 } 135 136 // NewWithClient allows for setting a custom http client (See New). 137 // An error is returned on invalid remote. The function panics when remote is nil. 138 func NewWithClient(remote, wsEndpoint string, client *http.Client) (*HTTP, error) { 139 if client == nil { 140 panic("nil http.Client provided") 141 } 142 143 rc, err := jsonrpcclient.NewCm39HttpJSONClient(remote, client) 144 if err != nil { 145 return nil, err 146 } 147 cdc := rc.Codec() 148 ctypes.RegisterAmino(cdc) 149 rc.SetCodec(cdc) 150 151 wsEvents, err := newWSEvents(cdc, remote, wsEndpoint) 152 if err != nil { 153 return nil, err 154 } 155 156 httpClient := &HTTP{ 157 rpc: rc, 158 remote: remote, 159 baseRPCClient: &baseRPCClient{caller: rc}, 160 WSEvents: wsEvents, 161 } 162 163 return httpClient, nil 164 } 165 166 var _ rpcclient.Client = (*HTTP)(nil) 167 168 // SetLogger sets a logger. 169 func (c *HTTP) SetLogger(l log.Logger) { 170 c.WSEvents.SetLogger(l) 171 } 172 173 // Remote returns the remote network address in a string form. 174 func (c *HTTP) Remote() string { 175 return c.remote 176 } 177 178 // NewBatch creates a new batch client for this HTTP client. 179 func (c *HTTP) NewBatch() *BatchHTTP { 180 rpcBatch := c.rpc.NewRequestBatch() 181 return &BatchHTTP{ 182 rpcBatch: rpcBatch, 183 baseRPCClient: &baseRPCClient{ 184 caller: rpcBatch, 185 }, 186 } 187 } 188 189 //----------------------------------------------------------------------------- 190 // BatchHTTP 191 192 // Send is a convenience function for an HTTP batch that will trigger the 193 // compilation of the batched requests and send them off using the client as a 194 // single request. On success, this returns a list of the deserialized results 195 // from each request in the sent batch. 196 func (b *BatchHTTP) Send() ([]interface{}, error) { 197 return b.rpcBatch.Send() 198 } 199 200 // Clear will empty out this batch of requests and return the number of requests 201 // that were cleared out. 202 func (b *BatchHTTP) Clear() int { 203 return b.rpcBatch.Clear() 204 } 205 206 // Count returns the number of enqueued requests waiting to be sent. 207 func (b *BatchHTTP) Count() int { 208 return b.rpcBatch.Count() 209 } 210 211 //----------------------------------------------------------------------------- 212 // baseRPCClient 213 214 func (c *baseRPCClient) Status() (*ctypes.ResultStatus, error) { 215 result := new(ctypes.ResultStatus) 216 _, err := c.caller.Call("status", map[string]interface{}{}, result) 217 if err != nil { 218 return nil, errors.Wrap(err, "Status") 219 } 220 return result, nil 221 } 222 223 func (c *baseRPCClient) ABCIInfo() (*ctypes.ResultABCIInfo, error) { 224 result := new(ctypes.ResultABCIInfo) 225 _, err := c.caller.Call("abci_info", map[string]interface{}{}, result) 226 if err != nil { 227 return nil, errors.Wrap(err, "ABCIInfo") 228 } 229 return result, nil 230 } 231 232 func (c *baseRPCClient) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) { 233 return c.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions) 234 } 235 236 func (c *baseRPCClient) ABCIQueryWithOptions( 237 path string, 238 data bytes.HexBytes, 239 opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { 240 result := new(ctypes.ResultABCIQuery) 241 _, err := c.caller.Call("abci_query", 242 map[string]interface{}{"path": path, "data": data, "height": opts.Height, "prove": opts.Prove}, 243 result) 244 if err != nil { 245 return nil, errors.Wrap(err, "ABCIQuery") 246 } 247 return result, nil 248 } 249 250 func (c *baseRPCClient) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { 251 result := new(ctypes.ResultBroadcastTxCommit) 252 _, err := c.caller.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result) 253 if err != nil { 254 return nil, errors.Wrap(err, "broadcast_tx_commit") 255 } 256 return result, nil 257 } 258 259 func (c *baseRPCClient) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 260 return c.broadcastTX("broadcast_tx_async", tx) 261 } 262 263 func (c *baseRPCClient) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 264 return c.broadcastTX("broadcast_tx_sync", tx) 265 } 266 267 func (c *baseRPCClient) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { 268 result := new(ctypes.ResultBroadcastTx) 269 _, err := c.caller.Call(route, map[string]interface{}{"tx": tx}, result) 270 if err != nil { 271 return nil, errors.Wrap(err, route) 272 } 273 return result, nil 274 } 275 276 func (c *baseRPCClient) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { 277 result := new(ctypes.ResultUnconfirmedTxs) 278 _, err := c.caller.Call("unconfirmed_txs", map[string]interface{}{"limit": limit}, result) 279 if err != nil { 280 return nil, errors.Wrap(err, "unconfirmed_txs") 281 } 282 return result, nil 283 } 284 285 func (c *baseRPCClient) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { 286 result := new(ctypes.ResultUnconfirmedTxs) 287 _, err := c.caller.Call("num_unconfirmed_txs", map[string]interface{}{}, result) 288 if err != nil { 289 return nil, errors.Wrap(err, "num_unconfirmed_txs") 290 } 291 return result, nil 292 } 293 294 func (c *baseRPCClient) UserUnconfirmedTxs(address string, limit int) (*ctypes.ResultUserUnconfirmedTxs, error) { 295 result := new(ctypes.ResultUserUnconfirmedTxs) 296 _, err := c.caller.Call("user_unconfirmed_txs", map[string]interface{}{"address": address, "limit": limit}, result) 297 if err != nil { 298 return nil, errors.Wrap(err, "user_unconfirmed_txs") 299 } 300 return result, nil 301 } 302 303 func (c *baseRPCClient) UserNumUnconfirmedTxs(address string) (*ctypes.ResultUserUnconfirmedTxs, error) { 304 result := new(ctypes.ResultUserUnconfirmedTxs) 305 _, err := c.caller.Call("user_num_unconfirmed_txs", map[string]interface{}{"address": address}, result) 306 if err != nil { 307 return nil, errors.Wrap(err, "user_num_unconfirmed_txs") 308 } 309 return result, nil 310 } 311 312 func (c *baseRPCClient) GetAddressList() (*ctypes.ResultUnconfirmedAddresses, error) { 313 result := new(ctypes.ResultUnconfirmedAddresses) 314 _, err := c.caller.Call("get_address_list", map[string]interface{}{}, result) 315 if err != nil { 316 return nil, errors.Wrap(err, "get_address_list") 317 } 318 return result, nil 319 } 320 321 func (c *baseRPCClient) GetPendingNonce(address string) (*ctypes.ResultPendingNonce, bool) { 322 result := new(ctypes.ResultPendingNonce) 323 _, err := c.caller.Call("get_pending_nonce", map[string]interface{}{"address": address}, result) 324 if err != nil { 325 return nil, false 326 } 327 return result, true 328 } 329 330 func (c *baseRPCClient) NetInfo() (*ctypes.ResultNetInfo, error) { 331 result := new(ctypes.ResultNetInfo) 332 _, err := c.caller.Call("net_info", map[string]interface{}{}, result) 333 if err != nil { 334 return nil, errors.Wrap(err, "NetInfo") 335 } 336 return result, nil 337 } 338 339 func (c *baseRPCClient) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { 340 result := new(ctypes.ResultDumpConsensusState) 341 _, err := c.caller.Call("dump_consensus_state", map[string]interface{}{}, result) 342 if err != nil { 343 return nil, errors.Wrap(err, "DumpConsensusState") 344 } 345 return result, nil 346 } 347 348 func (c *baseRPCClient) ConsensusState() (*ctypes.ResultConsensusState, error) { 349 result := new(ctypes.ResultConsensusState) 350 _, err := c.caller.Call("consensus_state", map[string]interface{}{}, result) 351 if err != nil { 352 return nil, errors.Wrap(err, "ConsensusState") 353 } 354 return result, nil 355 } 356 357 func (c *baseRPCClient) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) { 358 result := new(ctypes.ResultConsensusParams) 359 _, err := c.caller.Call("consensus_params", map[string]interface{}{"height": height}, result) 360 if err != nil { 361 return nil, errors.Wrap(err, "ConsensusParams") 362 } 363 return result, nil 364 } 365 366 func (c *baseRPCClient) Health() (*ctypes.ResultHealth, error) { 367 result := new(ctypes.ResultHealth) 368 _, err := c.caller.Call("health", map[string]interface{}{}, result) 369 if err != nil { 370 return nil, errors.Wrap(err, "Health") 371 } 372 return result, nil 373 } 374 375 func (c *baseRPCClient) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { 376 result := new(ctypes.ResultBlockchainInfo) 377 _, err := c.caller.Call("blockchain", 378 map[string]interface{}{"minHeight": minHeight, "maxHeight": maxHeight}, 379 result) 380 if err != nil { 381 return nil, errors.Wrap(err, "BlockchainInfo") 382 } 383 return result, nil 384 } 385 386 func (c *baseRPCClient) LatestBlockNumber() (int64, error) { 387 info, err := c.BlockchainInfo(0, 0) 388 if err != nil { 389 return 0, err 390 } 391 return info.LastHeight, nil 392 } 393 394 func (c *baseRPCClient) Genesis() (*ctypes.ResultGenesis, error) { 395 result := new(ctypes.ResultGenesis) 396 _, err := c.caller.Call("genesis", map[string]interface{}{}, result) 397 if err != nil { 398 return nil, errors.Wrap(err, "Genesis") 399 } 400 return result, nil 401 } 402 403 func (c *baseRPCClient) Block(height *int64) (*ctypes.ResultBlock, error) { 404 result := new(ctypes.ResultBlock) 405 _, err := c.caller.Call("block", map[string]interface{}{"height": height}, result) 406 if err != nil { 407 return nil, errors.Wrap(err, "Block") 408 } 409 return result, nil 410 } 411 412 func (c *baseRPCClient) BlockInfo(height *int64) (*types.BlockMeta, error) { 413 result := new(types.BlockMeta) 414 _, err := c.caller.Call("block_info", map[string]interface{}{"height": height}, result) 415 if err != nil { 416 return nil, errors.Wrap(err, "BlockInfo") 417 } 418 return result, nil 419 } 420 421 func (c *baseRPCClient) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) { 422 result := new(ctypes.ResultBlockResults) 423 _, err := c.caller.Call("block_results", map[string]interface{}{"height": height}, result) 424 if err != nil { 425 return nil, errors.Wrap(err, "Block Result") 426 } 427 return result, nil 428 } 429 430 func (c *baseRPCClient) Commit(height *int64) (*ctypes.ResultCommit, error) { 431 result := new(ctypes.IBCResultCommit) 432 _, err := c.caller.Call("commit", map[string]interface{}{"height": height}, result) 433 if err != nil { 434 return nil, errors.Wrap(err, "Commit") 435 } 436 return result.ToCommit(), nil 437 } 438 439 func (c *baseRPCClient) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { 440 result := new(ctypes.ResultTx) 441 params := map[string]interface{}{ 442 "hash": hash, 443 "prove": prove, 444 } 445 _, err := c.caller.Call("tx", params, result) 446 if err != nil { 447 return nil, errors.Wrap(err, "Tx") 448 } 449 return result, nil 450 } 451 452 func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage int, orderBy string) ( 453 *ctypes.ResultTxSearch, error) { 454 result := new(ctypes.ResultTxSearch) 455 params := map[string]interface{}{ 456 "query": query, 457 "prove": prove, 458 "page": page, 459 "per_page": perPage, 460 "order_by": orderBy, 461 } 462 _, err := c.caller.Call("tx_search", params, result) 463 if err != nil { 464 return nil, errors.Wrap(err, "TxSearch") 465 } 466 return result, nil 467 } 468 469 func (c *baseRPCClient) Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) { 470 result := new(ctypes.ResultValidators) 471 _, err := c.caller.Call("validators", map[string]interface{}{ 472 "height": height, 473 "page": page, 474 "per_page": perPage, 475 }, result) 476 if err != nil { 477 return nil, errors.Wrap(err, "Validators") 478 } 479 return result, nil 480 } 481 482 func (c *baseRPCClient) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) { 483 result := new(ctypes.ResultBroadcastEvidence) 484 _, err := c.caller.Call("broadcast_evidence", map[string]interface{}{"evidence": ev}, result) 485 if err != nil { 486 return nil, errors.Wrap(err, "BroadcastEvidence") 487 } 488 return result, nil 489 } 490 491 //----------------------------------------------------------------------------- 492 // WSEvents 493 494 var errNotRunning = errors.New("client is not running. Use .Start() method to start") 495 496 // WSEvents is a wrapper around WSClient, which implements EventsClient. 497 type WSEvents struct { 498 service.BaseService 499 cdc *amino.Codec 500 remote string 501 endpoint string 502 ws *jsonrpcclient.WSClient 503 504 mtx sync.RWMutex 505 subscriptions map[string]chan ctypes.ResultEvent // query -> chan 506 } 507 508 func newWSEvents(cdc *amino.Codec, remote, endpoint string) (*WSEvents, error) { 509 w := &WSEvents{ 510 cdc: cdc, 511 endpoint: endpoint, 512 remote: remote, 513 subscriptions: make(map[string]chan ctypes.ResultEvent), 514 } 515 w.BaseService = *service.NewBaseService(nil, "WSEvents", w) 516 517 var err error 518 w.ws, err = jsonrpcclient.NewWS(w.remote, w.endpoint, jsonrpcclient.OnReconnect(func() { 519 // resubscribe immediately 520 w.redoSubscriptionsAfter(0 * time.Second) 521 })) 522 if err != nil { 523 return nil, err 524 } 525 w.ws.SetCodec(w.cdc) 526 w.ws.SetLogger(w.Logger) 527 528 return w, nil 529 } 530 531 // OnStart implements service.Service by starting WSClient and event loop. 532 func (w *WSEvents) OnStart() error { 533 if err := w.ws.Start(); err != nil { 534 return err 535 } 536 537 go w.eventListener() 538 539 return nil 540 } 541 542 // OnStop implements service.Service by stopping WSClient. 543 func (w *WSEvents) OnStop() { 544 _ = w.ws.Stop() 545 } 546 547 // Subscribe implements EventsClient by using WSClient to subscribe given 548 // subscriber to query. By default, returns a channel with cap=1. Error is 549 // returned if it fails to subscribe. 550 // 551 // Channel is never closed to prevent clients from seeing an erroneous event. 552 // 553 // It returns an error if WSEvents is not running. 554 func (w *WSEvents) Subscribe(ctx context.Context, subscriber, query string, 555 outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) { 556 557 if !w.IsRunning() { 558 return nil, errNotRunning 559 } 560 561 if err := w.ws.Subscribe(ctx, query); err != nil { 562 return nil, err 563 } 564 565 outCap := 1 566 if len(outCapacity) > 0 { 567 outCap = outCapacity[0] 568 } 569 570 outc := make(chan ctypes.ResultEvent, outCap) 571 w.mtx.Lock() 572 // subscriber param is ignored because Tendermint will override it with 573 // remote IP anyway. 574 w.subscriptions[query] = outc 575 w.mtx.Unlock() 576 577 return outc, nil 578 } 579 580 // Unsubscribe implements EventsClient by using WSClient to unsubscribe given 581 // subscriber from query. 582 // 583 // It returns an error if WSEvents is not running. 584 func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber, query string) error { 585 if !w.IsRunning() { 586 return errNotRunning 587 } 588 589 if err := w.ws.Unsubscribe(ctx, query); err != nil { 590 return err 591 } 592 593 w.mtx.Lock() 594 _, ok := w.subscriptions[query] 595 if ok { 596 delete(w.subscriptions, query) 597 } 598 w.mtx.Unlock() 599 600 return nil 601 } 602 603 // UnsubscribeAll implements EventsClient by using WSClient to unsubscribe 604 // given subscriber from all the queries. 605 // 606 // It returns an error if WSEvents is not running. 607 func (w *WSEvents) UnsubscribeAll(ctx context.Context, subscriber string) error { 608 if !w.IsRunning() { 609 return errNotRunning 610 } 611 612 if err := w.ws.UnsubscribeAll(ctx); err != nil { 613 return err 614 } 615 616 w.mtx.Lock() 617 w.subscriptions = make(map[string]chan ctypes.ResultEvent) 618 w.mtx.Unlock() 619 620 return nil 621 } 622 623 // After being reconnected, it is necessary to redo subscription to server 624 // otherwise no data will be automatically received. 625 func (w *WSEvents) redoSubscriptionsAfter(d time.Duration) { 626 time.Sleep(d) 627 628 w.mtx.RLock() 629 defer w.mtx.RUnlock() 630 for q := range w.subscriptions { 631 err := w.ws.Subscribe(context.Background(), q) 632 if err != nil { 633 w.Logger.Error("Failed to resubscribe", "err", err) 634 } 635 } 636 } 637 638 func isErrAlreadySubscribed(err error) bool { 639 return strings.Contains(err.Error(), tmpubsub.ErrAlreadySubscribed.Error()) 640 } 641 642 func (w *WSEvents) eventListener() { 643 for { 644 select { 645 case resp, ok := <-w.ws.ResponsesCh: 646 if !ok { 647 return 648 } 649 650 if resp.Error != nil { 651 w.Logger.Error("WS error", "err", resp.Error.Error()) 652 // Error can be ErrAlreadySubscribed or max client (subscriptions per 653 // client) reached or Tendermint exited. 654 // We can ignore ErrAlreadySubscribed, but need to retry in other 655 // cases. 656 if !isErrAlreadySubscribed(resp.Error) { 657 // Resubscribe after 1 second to give Tendermint time to restart (if 658 // crashed). 659 w.redoSubscriptionsAfter(1 * time.Second) 660 } 661 continue 662 } 663 664 result := new(ctypes.ResultEvent) 665 err := w.cdc.UnmarshalJSON(resp.Result, result) 666 if err != nil { 667 w.Logger.Error("failed to unmarshal response", "err", err) 668 continue 669 } 670 671 w.mtx.RLock() 672 if out, ok := w.subscriptions[result.Query]; ok { 673 if cap(out) == 0 { 674 out <- *result 675 } else { 676 select { 677 case out <- *result: 678 default: 679 w.Logger.Error("wanted to publish ResultEvent, but out channel is full", "result", result, "query", result.Query) 680 } 681 } 682 } 683 w.mtx.RUnlock() 684 case <-w.Quit(): 685 return 686 } 687 } 688 }