|
package proxy
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/go-kit/kit/metrics"
|
|
abciclient "github.com/tendermint/tendermint/abci/client"
|
|
"github.com/tendermint/tendermint/abci/example/kvstore"
|
|
"github.com/tendermint/tendermint/abci/types"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
"github.com/tendermint/tendermint/libs/service"
|
|
e2e "github.com/tendermint/tendermint/test/e2e/app"
|
|
)
|
|
|
|
// ClientFactory returns a client object, which will create a local
|
|
// client if addr is one of: 'kvstore', 'persistent_kvstore', 'e2e',
|
|
// or 'noop', otherwise - a remote client.
|
|
//
|
|
// The Closer is a noop except for persistent_kvstore applications,
|
|
// which will clean up the store.
|
|
func ClientFactory(logger log.Logger, addr, transport, dbDir string) (abciclient.Client, io.Closer, error) {
|
|
switch addr {
|
|
case "kvstore":
|
|
return abciclient.NewLocalClient(logger, kvstore.NewApplication()), noopCloser{}, nil
|
|
case "persistent_kvstore":
|
|
app := kvstore.NewPersistentKVStoreApplication(logger, dbDir)
|
|
return abciclient.NewLocalClient(logger, app), app, nil
|
|
case "e2e":
|
|
app, err := e2e.NewApplication(e2e.DefaultConfig(dbDir))
|
|
if err != nil {
|
|
return nil, noopCloser{}, err
|
|
}
|
|
return abciclient.NewLocalClient(logger, app), noopCloser{}, nil
|
|
case "noop":
|
|
return abciclient.NewLocalClient(logger, types.NewBaseApplication()), noopCloser{}, nil
|
|
default:
|
|
const mustConnect = false // loop retrying
|
|
client, err := abciclient.NewClient(logger, addr, transport, mustConnect)
|
|
if err != nil {
|
|
return nil, noopCloser{}, err
|
|
}
|
|
|
|
return client, noopCloser{}, nil
|
|
}
|
|
}
|
|
|
|
type noopCloser struct{}
|
|
|
|
func (noopCloser) Close() error { return nil }
|
|
|
|
// proxyClient provides the application connection.
|
|
type proxyClient struct {
|
|
service.BaseService
|
|
logger log.Logger
|
|
|
|
client abciclient.Client
|
|
metrics *Metrics
|
|
}
|
|
|
|
// New creates a proxy application interface.
|
|
func New(client abciclient.Client, logger log.Logger, metrics *Metrics) abciclient.Client {
|
|
conn := &proxyClient{
|
|
logger: logger,
|
|
metrics: metrics,
|
|
client: client,
|
|
}
|
|
conn.BaseService = *service.NewBaseService(logger, "proxyClient", conn)
|
|
return conn
|
|
}
|
|
|
|
func (app *proxyClient) OnStop() { tryCallStop(app.client) }
|
|
func (app *proxyClient) Error() error { return app.client.Error() }
|
|
|
|
func tryCallStop(client abciclient.Client) {
|
|
if c, ok := client.(interface{ Stop() }); ok {
|
|
c.Stop()
|
|
}
|
|
}
|
|
|
|
func (app *proxyClient) OnStart(ctx context.Context) error {
|
|
var err error
|
|
defer func() {
|
|
if err != nil {
|
|
tryCallStop(app.client)
|
|
}
|
|
}()
|
|
|
|
// Kill Tendermint if the ABCI application crashes.
|
|
go func() {
|
|
if !app.client.IsRunning() {
|
|
return
|
|
}
|
|
app.client.Wait()
|
|
if ctx.Err() != nil {
|
|
return
|
|
}
|
|
|
|
if err := app.client.Error(); err != nil {
|
|
app.logger.Error("client connection terminated. Did the application crash? Please restart tendermint",
|
|
"err", err)
|
|
|
|
if killErr := kill(); killErr != nil {
|
|
app.logger.Error("Failed to kill this process - please do so manually",
|
|
"err", killErr)
|
|
}
|
|
}
|
|
|
|
}()
|
|
|
|
return app.client.Start(ctx)
|
|
}
|
|
|
|
func kill() error {
|
|
p, err := os.FindProcess(os.Getpid())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return p.Signal(syscall.SIGABRT)
|
|
}
|
|
|
|
func (app *proxyClient) InitChain(ctx context.Context, req types.RequestInitChain) (*types.ResponseInitChain, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "init_chain", "type", "sync"))()
|
|
return app.client.InitChain(ctx, req)
|
|
}
|
|
|
|
func (app *proxyClient) PrepareProposal(ctx context.Context, req types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "prepare_proposal", "type", "sync"))()
|
|
return app.client.PrepareProposal(ctx, req)
|
|
}
|
|
|
|
func (app *proxyClient) ProcessProposal(ctx context.Context, req types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "process_proposal", "type", "sync"))()
|
|
return app.client.ProcessProposal(ctx, req)
|
|
}
|
|
|
|
func (app *proxyClient) ExtendVote(ctx context.Context, req types.RequestExtendVote) (*types.ResponseExtendVote, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "extend_vote", "type", "sync"))()
|
|
return app.client.ExtendVote(ctx, req)
|
|
}
|
|
|
|
func (app *proxyClient) VerifyVoteExtension(ctx context.Context, req types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "verify_vote_extension", "type", "sync"))()
|
|
return app.client.VerifyVoteExtension(ctx, req)
|
|
}
|
|
|
|
func (app *proxyClient) FinalizeBlock(ctx context.Context, req types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "finalize_block", "type", "sync"))()
|
|
return app.client.FinalizeBlock(ctx, req)
|
|
}
|
|
|
|
func (app *proxyClient) Commit(ctx context.Context) (*types.ResponseCommit, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "commit", "type", "sync"))()
|
|
return app.client.Commit(ctx)
|
|
}
|
|
|
|
func (app *proxyClient) Flush(ctx context.Context) error {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "flush", "type", "sync"))()
|
|
return app.client.Flush(ctx)
|
|
}
|
|
|
|
func (app *proxyClient) CheckTx(ctx context.Context, req types.RequestCheckTx) (*types.ResponseCheckTx, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "check_tx", "type", "sync"))()
|
|
return app.client.CheckTx(ctx, req)
|
|
}
|
|
|
|
func (app *proxyClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "echo", "type", "sync"))()
|
|
return app.client.Echo(ctx, msg)
|
|
}
|
|
|
|
func (app *proxyClient) Info(ctx context.Context, req types.RequestInfo) (*types.ResponseInfo, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "info", "type", "sync"))()
|
|
return app.client.Info(ctx, req)
|
|
}
|
|
|
|
func (app *proxyClient) Query(ctx context.Context, reqQuery types.RequestQuery) (*types.ResponseQuery, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "query", "type", "sync"))()
|
|
return app.client.Query(ctx, reqQuery)
|
|
}
|
|
|
|
func (app *proxyClient) ListSnapshots(ctx context.Context, req types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "list_snapshots", "type", "sync"))()
|
|
return app.client.ListSnapshots(ctx, req)
|
|
}
|
|
|
|
func (app *proxyClient) OfferSnapshot(ctx context.Context, req types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "offer_snapshot", "type", "sync"))()
|
|
return app.client.OfferSnapshot(ctx, req)
|
|
}
|
|
|
|
func (app *proxyClient) LoadSnapshotChunk(ctx context.Context, req types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "load_snapshot_chunk", "type", "sync"))()
|
|
return app.client.LoadSnapshotChunk(ctx, req)
|
|
}
|
|
|
|
func (app *proxyClient) ApplySnapshotChunk(ctx context.Context, req types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
|
|
defer addTimeSample(app.metrics.MethodTiming.With("method", "apply_snapshot_chunk", "type", "sync"))()
|
|
return app.client.ApplySnapshotChunk(ctx, req)
|
|
}
|
|
|
|
// addTimeSample returns a function that, when called, adds an observation to m.
|
|
// The observation added to m is the number of seconds ellapsed since addTimeSample
|
|
// was initially called. addTimeSample is meant to be called in a defer to calculate
|
|
// the amount of time a function takes to complete.
|
|
func addTimeSample(m metrics.Histogram) func() {
|
|
start := time.Now()
|
|
return func() { m.Observe(time.Since(start).Seconds()) }
|
|
}
|