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  }