github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/abci/client/client.go (about) 1 package abcicli 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/tendermint/tendermint/abci/types" 8 9 ocabci "github.com/line/ostracon/abci/types" 10 "github.com/line/ostracon/libs/service" 11 tmsync "github.com/line/ostracon/libs/sync" 12 ) 13 14 //go:generate mockery --case underscore --name Client 15 const ( 16 dialRetryIntervalSeconds = 3 17 echoRetryIntervalSeconds = 1 18 ) 19 20 // Client defines an interface for an ABCI client. 21 // All `Async` methods return a `ReqRes` object. 22 // All `Sync` methods return the appropriate protobuf ResponseXxx struct and an error. 23 // Note these are client errors, eg. ABCI socket connectivity issues. 24 // Application-related errors are reflected in response via ABCI error codes and logs. 25 type Client interface { 26 service.Service 27 28 SetGlobalCallback(GlobalCallback) 29 GetGlobalCallback() GlobalCallback 30 Error() error 31 32 FlushAsync(ResponseCallback) *ReqRes 33 EchoAsync(string, ResponseCallback) *ReqRes 34 InfoAsync(types.RequestInfo, ResponseCallback) *ReqRes 35 SetOptionAsync(types.RequestSetOption, ResponseCallback) *ReqRes 36 DeliverTxAsync(types.RequestDeliverTx, ResponseCallback) *ReqRes 37 CheckTxAsync(types.RequestCheckTx, ResponseCallback) *ReqRes 38 QueryAsync(types.RequestQuery, ResponseCallback) *ReqRes 39 CommitAsync(ResponseCallback) *ReqRes 40 InitChainAsync(types.RequestInitChain, ResponseCallback) *ReqRes 41 BeginBlockAsync(ocabci.RequestBeginBlock, ResponseCallback) *ReqRes 42 EndBlockAsync(types.RequestEndBlock, ResponseCallback) *ReqRes 43 BeginRecheckTxAsync(ocabci.RequestBeginRecheckTx, ResponseCallback) *ReqRes 44 EndRecheckTxAsync(ocabci.RequestEndRecheckTx, ResponseCallback) *ReqRes 45 ListSnapshotsAsync(types.RequestListSnapshots, ResponseCallback) *ReqRes 46 OfferSnapshotAsync(types.RequestOfferSnapshot, ResponseCallback) *ReqRes 47 LoadSnapshotChunkAsync(types.RequestLoadSnapshotChunk, ResponseCallback) *ReqRes 48 ApplySnapshotChunkAsync(types.RequestApplySnapshotChunk, ResponseCallback) *ReqRes 49 50 FlushSync() (*types.ResponseFlush, error) 51 EchoSync(string) (*types.ResponseEcho, error) 52 InfoSync(types.RequestInfo) (*types.ResponseInfo, error) 53 SetOptionSync(types.RequestSetOption) (*types.ResponseSetOption, error) 54 DeliverTxSync(types.RequestDeliverTx) (*types.ResponseDeliverTx, error) 55 CheckTxSync(types.RequestCheckTx) (*ocabci.ResponseCheckTx, error) 56 QuerySync(types.RequestQuery) (*types.ResponseQuery, error) 57 CommitSync() (*types.ResponseCommit, error) 58 InitChainSync(types.RequestInitChain) (*types.ResponseInitChain, error) 59 BeginBlockSync(ocabci.RequestBeginBlock) (*types.ResponseBeginBlock, error) 60 EndBlockSync(types.RequestEndBlock) (*types.ResponseEndBlock, error) 61 BeginRecheckTxSync(ocabci.RequestBeginRecheckTx) (*ocabci.ResponseBeginRecheckTx, error) 62 EndRecheckTxSync(ocabci.RequestEndRecheckTx) (*ocabci.ResponseEndRecheckTx, error) 63 ListSnapshotsSync(types.RequestListSnapshots) (*types.ResponseListSnapshots, error) 64 OfferSnapshotSync(types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) 65 LoadSnapshotChunkSync(types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) 66 ApplySnapshotChunkSync(types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) 67 } 68 69 //---------------------------------------- 70 71 // NewClient returns a new ABCI client of the specified transport type. 72 // It returns an error if the transport is not "socket" or "grpc" 73 func NewClient(addr, transport string, mustConnect bool) (client Client, err error) { 74 switch transport { 75 case "socket": 76 client = NewSocketClient(addr, mustConnect) 77 case "grpc": 78 client = NewGRPCClient(addr, mustConnect) 79 default: 80 err = fmt.Errorf("unknown abci transport %s", transport) 81 } 82 return 83 } 84 85 type GlobalCallback func(*ocabci.Request, *ocabci.Response) 86 type ResponseCallback func(*ocabci.Response) 87 88 type ReqRes struct { 89 *ocabci.Request 90 *ocabci.Response // Not set atomically, so be sure to use WaitGroup. 91 92 mtx tmsync.Mutex 93 wg *sync.WaitGroup 94 done bool // Gets set to true once *after* WaitGroup.Done(). 95 cb ResponseCallback // A single callback that may be set. 96 } 97 98 func NewReqRes(req *ocabci.Request, cb ResponseCallback) *ReqRes { 99 return &ReqRes{ 100 Request: req, 101 Response: nil, 102 103 wg: waitGroup1(), 104 done: false, 105 cb: cb, 106 } 107 } 108 109 // InvokeCallback invokes a thread-safe execution of the configured callback 110 // if non-nil. 111 func (reqRes *ReqRes) InvokeCallback() { 112 reqRes.mtx.Lock() 113 defer reqRes.mtx.Unlock() 114 115 if reqRes.cb != nil { 116 reqRes.cb(reqRes.Response) 117 } 118 } 119 120 func (reqRes *ReqRes) SetDone(res *ocabci.Response) (set bool) { 121 reqRes.mtx.Lock() 122 // TODO should we panic if it's already done? 123 set = !reqRes.done 124 if set { 125 reqRes.Response = res 126 reqRes.done = true 127 reqRes.wg.Done() 128 } 129 reqRes.mtx.Unlock() 130 131 // NOTE `reqRes.cb` is immutable so we're safe to access it at here without `mtx` 132 if set && reqRes.cb != nil { 133 reqRes.cb(res) 134 } 135 136 return set 137 } 138 139 func (reqRes *ReqRes) Wait() { 140 reqRes.wg.Wait() 141 } 142 143 func waitGroup1() (wg *sync.WaitGroup) { 144 wg = &sync.WaitGroup{} 145 wg.Add(1) 146 return 147 }