github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/proxy/client.go (about) 1 package proxy 2 3 import ( 4 "context" 5 "io" 6 "os" 7 "syscall" 8 "time" 9 10 "github.com/go-kit/kit/metrics" 11 12 abciclient "github.com/ari-anchor/sei-tendermint/abci/client" 13 "github.com/ari-anchor/sei-tendermint/abci/example/kvstore" 14 "github.com/ari-anchor/sei-tendermint/abci/types" 15 "github.com/ari-anchor/sei-tendermint/libs/log" 16 "github.com/ari-anchor/sei-tendermint/libs/service" 17 e2e "github.com/ari-anchor/sei-tendermint/test/e2e/app" 18 ) 19 20 // ClientFactory returns a client object, which will create a local 21 // client if addr is one of: 'kvstore', 'persistent_kvstore', 'e2e', 22 // or 'noop', otherwise - a remote client. 23 // 24 // The Closer is a noop except for persistent_kvstore applications, 25 // which will clean up the store. 26 func ClientFactory(logger log.Logger, addr, transport, dbDir string) (abciclient.Client, io.Closer, error) { 27 switch addr { 28 case "kvstore": 29 return abciclient.NewLocalClient(logger, kvstore.NewApplication()), noopCloser{}, nil 30 case "persistent_kvstore": 31 app := kvstore.NewPersistentKVStoreApplication(logger, dbDir) 32 return abciclient.NewLocalClient(logger, app), app, nil 33 case "e2e": 34 app, err := e2e.NewApplication(e2e.DefaultConfig(dbDir)) 35 if err != nil { 36 return nil, noopCloser{}, err 37 } 38 return abciclient.NewLocalClient(logger, app), noopCloser{}, nil 39 case "noop": 40 return abciclient.NewLocalClient(logger, types.NewBaseApplication()), noopCloser{}, nil 41 default: 42 const mustConnect = false // loop retrying 43 client, err := abciclient.NewClient(logger, addr, transport, mustConnect) 44 if err != nil { 45 return nil, noopCloser{}, err 46 } 47 48 return client, noopCloser{}, nil 49 } 50 } 51 52 type noopCloser struct{} 53 54 func (noopCloser) Close() error { return nil } 55 56 // proxyClient provides the application connection. 57 type proxyClient struct { 58 service.BaseService 59 logger log.Logger 60 61 client abciclient.Client 62 metrics *Metrics 63 } 64 65 // New creates a proxy application interface. 66 func New(client abciclient.Client, logger log.Logger, metrics *Metrics) abciclient.Client { 67 conn := &proxyClient{ 68 logger: logger, 69 metrics: metrics, 70 client: client, 71 } 72 conn.BaseService = *service.NewBaseService(logger, "proxyClient", conn) 73 return conn 74 } 75 76 func (app *proxyClient) OnStop() { tryCallStop(app.client) } 77 func (app *proxyClient) Error() error { return app.client.Error() } 78 79 func tryCallStop(client abciclient.Client) { 80 if c, ok := client.(interface{ Stop() }); ok { 81 c.Stop() 82 } 83 } 84 85 func (app *proxyClient) OnStart(ctx context.Context) error { 86 var err error 87 defer func() { 88 if err != nil { 89 tryCallStop(app.client) 90 } 91 }() 92 93 // Kill Tendermint if the ABCI application crashes. 94 go func() { 95 if !app.client.IsRunning() { 96 return 97 } 98 app.client.Wait() 99 if ctx.Err() != nil { 100 return 101 } 102 103 if err := app.client.Error(); err != nil { 104 app.logger.Error("client connection terminated. Did the application crash? Please restart tendermint", 105 "err", err) 106 107 if killErr := kill(); killErr != nil { 108 app.logger.Error("Failed to kill this process - please do so manually", 109 "err", killErr) 110 } 111 } 112 113 }() 114 115 return app.client.Start(ctx) 116 } 117 118 func kill() error { 119 p, err := os.FindProcess(os.Getpid()) 120 if err != nil { 121 return err 122 } 123 124 return p.Signal(syscall.SIGABRT) 125 } 126 127 func (app *proxyClient) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) { 128 defer addTimeSample(app.metrics.MethodTiming.With("method", "init_chain", "type", "sync"))() 129 return app.client.InitChain(ctx, req) 130 } 131 132 func (app *proxyClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { 133 defer addTimeSample(app.metrics.MethodTiming.With("method", "prepare_proposal", "type", "sync"))() 134 return app.client.PrepareProposal(ctx, req) 135 } 136 137 func (app *proxyClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { 138 defer addTimeSample(app.metrics.MethodTiming.With("method", "process_proposal", "type", "sync"))() 139 return app.client.ProcessProposal(ctx, req) 140 } 141 142 func (app *proxyClient) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) { 143 defer addTimeSample(app.metrics.MethodTiming.With("method", "extend_vote", "type", "sync"))() 144 return app.client.ExtendVote(ctx, req) 145 } 146 147 func (app *proxyClient) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) { 148 defer addTimeSample(app.metrics.MethodTiming.With("method", "verify_vote_extension", "type", "sync"))() 149 return app.client.VerifyVoteExtension(ctx, req) 150 } 151 152 func (app *proxyClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { 153 defer addTimeSample(app.metrics.MethodTiming.With("method", "finalize_block", "type", "sync"))() 154 return app.client.FinalizeBlock(ctx, req) 155 } 156 157 func (app *proxyClient) LoadLatest(ctx context.Context, req *types.RequestLoadLatest) (*types.ResponseLoadLatest, error) { 158 defer addTimeSample(app.metrics.MethodTiming.With("method", "load_latest", "type", "sync"))() 159 return app.client.LoadLatest(ctx, req) 160 } 161 162 func (app *proxyClient) Commit(ctx context.Context) (*types.ResponseCommit, error) { 163 defer addTimeSample(app.metrics.MethodTiming.With("method", "commit", "type", "sync"))() 164 return app.client.Commit(ctx) 165 } 166 167 func (app *proxyClient) Flush(ctx context.Context) error { 168 defer addTimeSample(app.metrics.MethodTiming.With("method", "flush", "type", "sync"))() 169 return app.client.Flush(ctx) 170 } 171 172 func (app *proxyClient) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) { 173 defer addTimeSample(app.metrics.MethodTiming.With("method", "check_tx", "type", "sync"))() 174 return app.client.CheckTx(ctx, req) 175 } 176 177 func (app *proxyClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) { 178 defer addTimeSample(app.metrics.MethodTiming.With("method", "echo", "type", "sync"))() 179 return app.client.Echo(ctx, msg) 180 } 181 182 func (app *proxyClient) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) { 183 defer addTimeSample(app.metrics.MethodTiming.With("method", "info", "type", "sync"))() 184 return app.client.Info(ctx, req) 185 } 186 187 func (app *proxyClient) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) { 188 defer addTimeSample(app.metrics.MethodTiming.With("method", "query", "type", "sync"))() 189 return app.client.Query(ctx, req) 190 } 191 192 func (app *proxyClient) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { 193 defer addTimeSample(app.metrics.MethodTiming.With("method", "list_snapshots", "type", "sync"))() 194 return app.client.ListSnapshots(ctx, req) 195 } 196 197 func (app *proxyClient) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { 198 defer addTimeSample(app.metrics.MethodTiming.With("method", "offer_snapshot", "type", "sync"))() 199 return app.client.OfferSnapshot(ctx, req) 200 } 201 202 func (app *proxyClient) LoadSnapshotChunk(ctx context.Context, req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { 203 defer addTimeSample(app.metrics.MethodTiming.With("method", "load_snapshot_chunk", "type", "sync"))() 204 return app.client.LoadSnapshotChunk(ctx, req) 205 } 206 207 func (app *proxyClient) ApplySnapshotChunk(ctx context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { 208 defer addTimeSample(app.metrics.MethodTiming.With("method", "apply_snapshot_chunk", "type", "sync"))() 209 return app.client.ApplySnapshotChunk(ctx, req) 210 } 211 212 // addTimeSample returns a function that, when called, adds an observation to m. 213 // The observation added to m is the number of seconds ellapsed since addTimeSample 214 // was initially called. addTimeSample is meant to be called in a defer to calculate 215 // the amount of time a function takes to complete. 216 func addTimeSample(m metrics.Histogram) func() { 217 start := time.Now() 218 return func() { m.Observe(time.Since(start).Seconds()) } 219 }